Re: snmpd: Move SNMPv2-SMI::snmpV2 to application_internal.c
On Sun, 2023-10-29 at 20:11 +0100, Martijn van Duren wrote: > Similar reasoning and questions as the move of SNMPv2-MIB::snmp. > This moves SNMP-FRAMEWORK-MIB::snmpEngine and > SNMP-USER-BASED-SM-MIB::usmStats from mib.c to application_internal.c, > under SNMPv2-SMI::snmpV2. The reason for this broader umbrella is that > other backends have no business fiddling around under this region. > Since different backends bite eachother with exclusive regions, both > snmpEngine and usmStats must be moved at the same time. > > This change also breaks snmpd.sh from regress. This places a few custom > OIDs in the usmUserTable, which is under snmpV2. Since these tests are > mostly there to test snmp(1) I think it's worth removing these tests and > build a proper snmp(1) regress suite another day (regress part in next > mail) > > OK? > > martijn@ > Here's the regress diff Index: snmpd.sh === RCS file: /cvs/src/regress/usr.sbin/snmpd/snmpd.sh,v retrieving revision 1.18 diff -u -p -r1.18 snmpd.sh --- snmpd.sh19 Jan 2022 11:02:38 - 1.18 +++ snmpd.sh29 Oct 2023 19:14:33 - @@ -271,8 +271,6 @@ read-write community non-default-rw oid 1.3.6.1.4.1.30155.42.1 name myName read-only string "humppa" oid 1.3.6.1.4.1.30155.42.2 name myStatus read-only integer 1 # No need to place a full index, we just need the object -oid 1.3.6.1.6.3.15.1.2.2.1.3.1 name Reyk read-only string "Reyk Fl${oe}ter" -oid 1.3.6.1.6.3.15.1.2.2.1.3.2 name broken read-only string "br${boe}ken" EOF (cd ${OBJDIR} && nohup snmpd -dvf ./snmpd.conf > snmpd.log 2>&1) & @@ -344,38 +342,6 @@ fi # echo "Setting of a ro custom oid test unexpectedly succeeded." # FAILED=1 #fi - -snmp_command="snmp get -Oqv -v2c -c non-default-rw localhost \ -usmUserSecurityName.1.0" -echo === $snmp_command -reyk="$(eval LC_ALL=en_US.UTF-8 $snmp_command)" -if [ "$reyk" != "Reyk Fl${oe}ter" ] -then - echo "Printing of UTF-8 string in UTF-8 locale failed" - FAILED=1 -fi -reyk="$(eval LC_ALL=C $snmp_command)" -if [ "$reyk" != "Reyk Fl.ter" ] -then - echo "Printing of UTF-8 string in C locale failed" - FAILED=1 -fi - -snmp_command="snmp get -Oqv -v2c -c non-default-rw localhost \ -usmUserSecurityName.2.0" -echo === $snmp_command -broken="$(eval LC_ALL=en_US.UTF-8 $snmp_command)" -if [ "$broken" != "br${replacement}ken" ] -then - echo "Printing of UTF-8 replacement character failed" - FAILED=1 -fi -broken="$(eval LC_ALL=C $snmp_command)" -if [ "$broken" != "br?ken" ] -then - echo "Printing of question mark in C locale failed" - FAILED=1 -fi kill $(pgrep snmpd) >/dev/null 2>&1
snmpd: Move SNMPv2-MIB::system to application_internal.c
This one is a little more involved than the previous 2. Since the snmpd.conf's system variables are stored inside a 'struct oid', I want to move these into their own struct inside 'struct snmpd'. If we also move mib_tree to smi.c we can completely remove mib.c As stated in my previous mail, this removes the sysORTable. I'll add these back later in a proper way. (mib.c removal omitted from diff) With these the only consumer of mps.c left is the oid keyword of snmpd.conf. OK? martijn@ diff --git a/Makefile b/Makefile index 3522a38..98bdda5 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,7 @@ MAN=snmpd.8 snmpd.conf.5 SRCS= parse.y log.c snmpe.c application.c application_legacy.c \ application_blocklist.c application_internal.c \ application_agentx.c ax.c \ - mps.c trap.c mib.c smi.c snmpd.c \ + mps.c trap.c smi.c snmpd.c \ proc.c usm.c traphandler.c util.c LDADD= -levent -lutil -lcrypto diff --git a/application_internal.c b/application_internal.c index 36ab8ef..e682cc7 100644 --- a/application_internal.c +++ b/application_internal.c @@ -47,6 +47,7 @@ void appl_internal_getnext(struct appl_backend *, int32_t, int32_t, struct ber_element *appl_internal_snmp(struct ber_oid *); struct ber_element *appl_internal_engine(struct ber_oid *); struct ber_element *appl_internal_usmstats(struct ber_oid *); +struct ber_element *appl_internal_system(struct ber_oid *); struct appl_internal_object *appl_internal_object_parent(struct ber_oid *); int appl_internal_object_cmp(struct appl_internal_object *, struct appl_internal_object *); @@ -73,6 +74,15 @@ RB_PROTOTYPE_STATIC(appl_internal_objects, appl_internal_object, entry, void appl_internal_init(void) { + appl_internal_region(&OID(MIB_system)); + appl_internal_object(&OID(MIB_sysDescr), appl_internal_system, NULL); + appl_internal_object(&OID(MIB_sysOID), appl_internal_system, NULL); + appl_internal_object(&OID(MIB_sysUpTime), appl_internal_system, NULL); + appl_internal_object(&OID(MIB_sysContact), appl_internal_system, NULL); + appl_internal_object(&OID(MIB_sysName), appl_internal_system, NULL); + appl_internal_object(&OID(MIB_sysLocation), appl_internal_system, NULL); + appl_internal_object(&OID(MIB_sysServices), appl_internal_system, NULL); + appl_internal_region(&OID(MIB_snmp)); appl_internal_object(&OID(MIB_snmpInPkts), appl_internal_snmp, NULL); appl_internal_object(&OID(MIB_snmpOutPkts), appl_internal_snmp, NULL); @@ -441,6 +451,30 @@ appl_internal_usmstats(struct ber_oid *oid) return value; } +struct ber_element * +appl_internal_system(struct ber_oid *oid) +{ + struct snmp_system *s = &snmpd_env->sc_system; + struct ber_element *value = NULL; + + if (ober_oid_cmp(&OID(MIB_sysDescr, 0), oid) == 0) + return ober_add_string(NULL, s->sys_descr); + else if (ober_oid_cmp(&OID(MIB_sysOID, 0), oid) == 0) + return ober_add_oid(NULL, &s->sys_oid); + else if (ober_oid_cmp(&OID(MIB_sysUpTime, 0), oid) == 0) { + value = ober_add_integer(NULL, smi_getticks()); + ober_set_header(value, BER_CLASS_APPLICATION, SNMP_T_TIMETICKS); + } else if (ober_oid_cmp(&OID(MIB_sysContact, 0), oid) == 0) + return ober_add_string(NULL, s->sys_contact); + else if (ober_oid_cmp(&OID(MIB_sysName, 0), oid) == 0) + return ober_add_string(NULL, s->sys_name); + else if (ober_oid_cmp(&OID(MIB_sysLocation, 0), oid) == 0) + return ober_add_string(NULL, s->sys_location); + else if (ober_oid_cmp(&OID(MIB_sysServices, 0), oid) == 0) + return ober_add_integer(NULL, s->sys_services); + return value; +} + struct appl_internal_object * appl_internal_object_parent(struct ber_oid *oid) { diff --git a/parse.y b/parse.y index 8f9bad0..7ae7ce1 100644 --- a/parse.y +++ b/parse.y @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -760,28 +761,87 @@ system: SYSTEM sysmib ; sysmib : CONTACT STRING{ - struct ber_oid o = OID(MIB_sysContact); - mps_set(&o, $2, strlen($2)); + if (conf->sc_system.sys_contact[0] != '\0') { + yyerror("system contact already defined"); + free($2); + YYERROR; + } + if (strlcpy(conf->sc_system.sys_contact, $2, + sizeof(conf->sc_system.sys_contact)) >= + sizeof(conf->sc_system.sys_contact)) { + yyerror("system contact too long"); + free($2); + YYERROR; +
snmpd: Move SNMPv2-SMI::snmpV2 to application_internal.c
Similar reasoning and questions as the move of SNMPv2-MIB::snmp. This moves SNMP-FRAMEWORK-MIB::snmpEngine and SNMP-USER-BASED-SM-MIB::usmStats from mib.c to application_internal.c, under SNMPv2-SMI::snmpV2. The reason for this broader umbrella is that other backends have no business fiddling around under this region. Since different backends bite eachother with exclusive regions, both snmpEngine and usmStats must be moved at the same time. This change also breaks snmpd.sh from regress. This places a few custom OIDs in the usmUserTable, which is under snmpV2. Since these tests are mostly there to test snmp(1) I think it's worth removing these tests and build a proper snmp(1) regress suite another day (regress part in next mail) OK? martijn@ diff --git a/application_internal.c b/application_internal.c index c76e8ef..36ab8ef 100644 --- a/application_internal.c +++ b/application_internal.c @@ -45,6 +45,8 @@ void appl_internal_get(struct appl_backend *, int32_t, int32_t, const char *, void appl_internal_getnext(struct appl_backend *, int32_t, int32_t, const char *, struct appl_varbind *); struct ber_element *appl_internal_snmp(struct ber_oid *); +struct ber_element *appl_internal_engine(struct ber_oid *); +struct ber_element *appl_internal_usmstats(struct ber_oid *); struct appl_internal_object *appl_internal_object_parent(struct ber_oid *); int appl_internal_object_cmp(struct appl_internal_object *, struct appl_internal_object *); @@ -128,6 +130,29 @@ appl_internal_init(void) NULL); appl_internal_object(&OID(MIB_snmpProxyDrops), appl_internal_snmp, NULL); + + appl_internal_region(&OID(MIB_snmpV2)); + appl_internal_object(&OID(MIB_snmpEngineID), appl_internal_engine, + NULL); + appl_internal_object(&OID(MIB_snmpEngineBoots), appl_internal_engine, + NULL); + appl_internal_object(&OID(MIB_snmpEngineTime), appl_internal_engine, + NULL); + appl_internal_object(&OID(MIB_snmpEngineMaxMsgSize), + appl_internal_engine, NULL); + + appl_internal_object(&OID(MIB_usmStatsUnsupportedSecLevels), + appl_internal_usmstats, NULL); + appl_internal_object(&OID(MIB_usmStatsNotInTimeWindow), + appl_internal_usmstats, NULL); + appl_internal_object(&OID(MIB_usmStatsUnknownUserNames), + appl_internal_usmstats, NULL); + appl_internal_object(&OID(MIB_usmStatsUnknownEngineId), + appl_internal_usmstats, NULL); + appl_internal_object(&OID(MIB_usmStatsWrongDigests), + appl_internal_usmstats, NULL); + appl_internal_object(&OID(MIB_usmStatsDecryptionErrors), + appl_internal_usmstats, NULL); } void @@ -376,6 +401,46 @@ appl_internal_snmp(struct ber_oid *oid) return value; } +struct ber_element * +appl_internal_engine(struct ber_oid *oid) +{ + if (ober_oid_cmp(&OID(MIB_snmpEngineID, 0), oid) == 0) + return ober_add_nstring(NULL, snmpd_env->sc_engineid, + snmpd_env->sc_engineid_len); + else if (ober_oid_cmp(&OID(MIB_snmpEngineBoots, 0), oid) == 0) + return ober_add_integer(NULL, snmpd_env->sc_engine_boots); + else if (ober_oid_cmp(&OID(MIB_snmpEngineTime, 0), oid) == 0) + return ober_add_integer(NULL, snmpd_engine_time()); + else if (ober_oid_cmp(&OID(MIB_snmpEngineMaxMsgSize, 0), oid) == 0) + return ober_add_integer(NULL, READ_BUF_SIZE); + return NULL; +} + +struct ber_element * +appl_internal_usmstats(struct ber_oid *oid) +{ + struct snmp_stats *stats = &snmpd_env->sc_stats; + struct ber_element *value = NULL; + + if (ober_oid_cmp(&OID(MIB_usmStatsUnsupportedSecLevels, 0), oid) == 0) + value = ober_add_integer(NULL, stats->snmp_usmbadseclevel); + else if (ober_oid_cmp(&OID(MIB_usmStatsNotInTimeWindow, 0), oid) == 0) + value = ober_add_integer(NULL, stats->snmp_usmtimewindow); + else if (ober_oid_cmp(&OID(MIB_usmStatsUnknownUserNames, 0), oid) == 0) + value = ober_add_integer(NULL, stats->snmp_usmnosuchuser); + else if (ober_oid_cmp(&OID(MIB_usmStatsUnknownEngineId, 0), oid) == 0) + value = ober_add_integer(NULL, stats->snmp_usmnosuchengine); + else if (ober_oid_cmp(&OID(MIB_usmStatsWrongDigests, 0), oid) == 0) + value = ober_add_integer(NULL, stats->snmp_usmwrongdigest); + else if (ober_oid_cmp(&OID(MIB_usmStatsDecryptionErrors, 0), oid) == 0) + value = ober_add_integer(NULL, stats->snmp_usmdecrypterr); + + if (value != NULL) + ober_set_header(value, BER_CLASS_APPLICATION, SNMP_T_COUNTER32); + + return value; +} + struct appl_internal_object * appl_internal_object_parent(struct ber_oid *oid) { diff --git a/mib.c b/mib.c index ef34983..481460c 100644 --- a/mib.c +++ b/mib.c @@ -216,79 +216,6 @@ mib_sysor(struct oid *oid, struct ber_oid *o, struct ber_elem
snmpd: Move SNMPv2-MIB::snmp to application_internal.c
I think the subject says it all. I'm not a 100% convinced that appl_internal_snmp() should live in application_internal.c, maybe snmpe.c, where these metrics are set is a better place. But since things come from mib.c, let's keep it simply here and we can always shuffle the deckchairs later. OK? martijn@ diff --git a/application_internal.c b/application_internal.c index ff9611e..c76e8ef 100644 --- a/application_internal.c +++ b/application_internal.c @@ -44,6 +44,7 @@ void appl_internal_get(struct appl_backend *, int32_t, int32_t, const char *, struct appl_varbind *); void appl_internal_getnext(struct appl_backend *, int32_t, int32_t, const char *, struct appl_varbind *); +struct ber_element *appl_internal_snmp(struct ber_oid *); struct appl_internal_object *appl_internal_object_parent(struct ber_oid *); int appl_internal_object_cmp(struct appl_internal_object *, struct appl_internal_object *); @@ -70,6 +71,63 @@ RB_PROTOTYPE_STATIC(appl_internal_objects, appl_internal_object, entry, void appl_internal_init(void) { + appl_internal_region(&OID(MIB_snmp)); + appl_internal_object(&OID(MIB_snmpInPkts), appl_internal_snmp, NULL); + appl_internal_object(&OID(MIB_snmpOutPkts), appl_internal_snmp, NULL); + appl_internal_object(&OID(MIB_snmpInBadVersions), appl_internal_snmp, + NULL); + appl_internal_object(&OID(MIB_snmpInBadCommunityNames), + appl_internal_snmp, NULL); + appl_internal_object(&OID(MIB_snmpInBadCommunityUses), + appl_internal_snmp, NULL); + appl_internal_object(&OID(MIB_snmpInASNParseErrs), appl_internal_snmp, + NULL); + appl_internal_object(&OID(MIB_snmpInTooBigs), appl_internal_snmp, NULL); + appl_internal_object(&OID(MIB_snmpInNoSuchNames), appl_internal_snmp, + NULL); + appl_internal_object(&OID(MIB_snmpInBadValues), appl_internal_snmp, + NULL); + appl_internal_object(&OID(MIB_snmpInReadOnlys), appl_internal_snmp, + NULL); + appl_internal_object(&OID(MIB_snmpInReadOnlys), appl_internal_snmp, + NULL); + appl_internal_object(&OID(MIB_snmpInGenErrs), appl_internal_snmp, NULL); + appl_internal_object(&OID(MIB_snmpInTotalReqVars), appl_internal_snmp, + NULL); + appl_internal_object(&OID(MIB_snmpInTotalSetVars), appl_internal_snmp, + NULL); + appl_internal_object(&OID(MIB_snmpInGetRequests), appl_internal_snmp, + NULL); + appl_internal_object(&OID(MIB_snmpInGetNexts), appl_internal_snmp, + NULL); + appl_internal_object(&OID(MIB_snmpInSetRequests), appl_internal_snmp, + NULL); + appl_internal_object(&OID(MIB_snmpInGetResponses), appl_internal_snmp, + NULL); + appl_internal_object(&OID(MIB_snmpInTraps), appl_internal_snmp, NULL); + appl_internal_object(&OID(MIB_snmpOutTooBigs), appl_internal_snmp, + NULL); + appl_internal_object(&OID(MIB_snmpOutNoSuchNames), appl_internal_snmp, + NULL); + appl_internal_object(&OID(MIB_snmpOutBadValues), appl_internal_snmp, + NULL); + appl_internal_object(&OID(MIB_snmpOutGenErrs), appl_internal_snmp, + NULL); + appl_internal_object(&OID(MIB_snmpOutGetRequests), appl_internal_snmp, + NULL); + appl_internal_object(&OID(MIB_snmpOutGetNexts), appl_internal_snmp, + NULL); + appl_internal_object(&OID(MIB_snmpOutSetRequests), appl_internal_snmp, + NULL); + appl_internal_object(&OID(MIB_snmpOutGetResponses), appl_internal_snmp, + NULL); + appl_internal_object(&OID(MIB_snmpOutTraps), appl_internal_snmp, NULL); + appl_internal_object(&OID(MIB_snmpEnableAuthenTraps), + appl_internal_snmp, NULL); + appl_internal_object(&OID(MIB_snmpSilentDrops), appl_internal_snmp, + NULL); + appl_internal_object(&OID(MIB_snmpProxyDrops), appl_internal_snmp, + NULL); } void @@ -245,6 +303,79 @@ appl_internal_getnext(struct appl_backend *backend, appl_response(backend, requestid, APPL_ERROR_GENERR, i + 1, vblist); } +struct ber_element * +appl_internal_snmp(struct ber_oid *oid) +{ + struct snmp_stats *stats = &snmpd_env->sc_stats; + struct ber_element *value = NULL; + + if (ober_oid_cmp(oid, &OID(MIB_snmpEnableAuthenTraps, 0)) == 0) + return ober_add_integer(NULL, + stats->snmp_enableauthentraps ? 1 : 2); + if (ober_oid_cmp(&OID(MIB_snmpInPkts, 0), oid) == 0) + value = ober_add_integer(NULL, stats->snmp_inpkts); + else if (ober_oid_cmp(&OID(MIB_snmpOutPkts, 0), oid) == 0) + value = ober_add_integer(NULL, stats->snmp_outpkts); + else if (ober_oid_cmp(&OID(MIB_snmpInBadVersions, 0), oid) == 0) + value = ober_add_integer(NULL, stats->snmp_inbadversions); + else if (ober_oid_cmp(&OID(MIB_snmpInBadCommunityNames, 0),
snmpd: introduce application_internal.c
In my quest to move away from the old code of mps.c and mib.c, here's the next step: Introduce a new application_internal.c. This is basically a very simplified version of the libagentx interface. We register a region at a certain point with application.c, and use an internal database of objects that can be walked like leaves that application.c doesn't know about. Apart from the code being a little easier to read than mps.c (imho), this new interface has a few advantages over application_legacy.c: - no objects are registered inside application.c as instance. Since application.c needs to account for overlapping regions it's quite a bit slower in finding the correct regions. appl_internal just deals with objects, which means we can just walk the leaves. This also makes the startup logs a bit quieter. - Since we can now register an entire region we can block reserved regions from being claimed by other backends. - We can now better distinguish between noSuchObject and noSuchInstance for GetRequests. Since our only internal table atm is sysORTable, which only contains invalid values I left out struct appl_internal_object's getnext() call, but it should be "easy" enough to add later (I have some code here that needs a bit of an extra shine before sending out). getnext() is going to be called for non-scalars, where struct appl_internal_object can't know the index. This diff just adds the plumbing. Migrating the remaining mib.c code will be done in 3 followup diffs, after which mib.c can be removed. mps.c does need to stick around for a little longer, because of the oid keyword in snmpd.conf. OK? martijn@ diff --git a/Makefile b/Makefile index 261e89b..3522a38 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ PROG= snmpd MAN= snmpd.8 snmpd.conf.5 SRCS= parse.y log.c snmpe.c application.c application_legacy.c \ - application_blocklist.c \ + application_blocklist.c application_internal.c \ application_agentx.c ax.c \ mps.c trap.c mib.c smi.c snmpd.c \ proc.c usm.c traphandler.c util.c diff --git a/application.c b/application.c index c36f059..2359943 100644 --- a/application.c +++ b/application.c @@ -159,6 +159,7 @@ void appl_init(void) { appl_blocklist_init(); + appl_internal_init(); appl_legacy_init(); appl_agentx_init(); } @@ -169,6 +170,7 @@ appl_shutdown(void) struct appl_context *ctx, *tctx; appl_blocklist_shutdown(); + appl_internal_shutdown(); appl_legacy_shutdown(); appl_agentx_shutdown(); diff --git a/application.h b/application.h index 39e7b5f..b9c95c0 100644 --- a/application.h +++ b/application.h @@ -147,3 +147,7 @@ void appl_agentx_backend(int); /* application_blocklist.c */ voidappl_blocklist_init(void); voidappl_blocklist_shutdown(void); + +/* application_internal.c */ +voidappl_internal_init(void); +voidappl_internal_shutdown(void); diff --git a/application_internal.c b/application_internal.c new file mode 100644 index 000..ff9611e --- /dev/null +++ b/application_internal.c @@ -0,0 +1,271 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2023 Martijn van Duren + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include +#include + +#include "application.h" +#include "log.h" +#include "mib.h" +#include "smi.h" +#include "snmpd.h" + +struct appl_internal_object { + struct ber_oid oid; + struct ber_element *(*get)(struct ber_oid *); + /* No getnext means the object is scalar */ + struct ber_element *(*getnext)(int8_t, struct ber_oid *); + + RB_ENTRY(appl_internal_object) entry; +}; + +void appl_internal_region(struct ber_oid *); +void appl_internal_object(struct ber_oid *, +struct ber_element *(*)(struct ber_oid *), +struct ber_element *(*)(int8_t, struct ber_oid *)); +void appl_internal_get(struct appl_backend *, int32_t, int32_t, const char *, +struct appl_varbind *); +void appl_internal_getnext(struct appl_backend *, int32_t, int32_t, +const char *, struct app
snmpd: don't move back in oid tree when on/below instance below region
Right now when we register a region with below that an instance we can revert back in the tree. When we request below the instance we currently use appl_region_next() to find the next region and simply set the to be find oid to the the oid of the new region. In the situation described above this means that we return the parent region and set the oid to that of the parent region, which is smaller than the instance oid. The easiest fix is to increment the searched oid to nextsibling of the instance and let the normal appl_varbind_backend() logic handle it from there. OK? martijn@ Index: usr.sbin/snmpd/application.c === RCS file: /cvs/src/usr.sbin/snmpd/application.c,v retrieving revision 1.25 diff -u -p -r1.25 application.c --- usr.sbin/snmpd/application.c27 Oct 2023 10:32:11 - 1.25 +++ usr.sbin/snmpd/application.c28 Oct 2023 07:57:23 - @@ -1368,19 +1368,17 @@ appl_varbind_backend(struct appl_varbind return -1; return 0; } - if ((region = appl_region_next(ureq->aru_ctx, - &(vb->av_oid), region)) == NULL) - goto eomv; vb->av_oid = region->ar_oid; + ober_oid_nextsibling(&(vb->av_oid)); vb->av_include = 1; + return appl_varbind_backend(ivb); } } else if (cmp == 0) { if (region->ar_instance && next && !vb->av_include) { - if ((region = appl_region_next(ureq->aru_ctx, - &(vb->av_oid), region)) == NULL) - goto eomv; vb->av_oid = region->ar_oid; + ober_oid_nextsibling(&(vb->av_oid)); vb->av_include = 1; + return appl_varbind_backend(ivb); } } ivb->avi_region = region; Index: regress/usr.sbin/snmpd/Makefile === RCS file: /cvs/src/regress/usr.sbin/snmpd/Makefile,v retrieving revision 1.6 diff -u -p -r1.6 Makefile --- regress/usr.sbin/snmpd/Makefile 27 Oct 2023 10:26:20 - 1.6 +++ regress/usr.sbin/snmpd/Makefile 28 Oct 2023 07:57:23 - @@ -170,6 +170,9 @@ BACKEND_TARGETS+= backend_getnext_stale BACKEND_TARGETS+= backend_getnext_inclusive_backwards BACKEND_TARGETS+= backend_getnext_toofew BACKEND_TARGETS+= backend_getnext_toomany +BACKEND_TARGETS+= backend_getnext_instance_below_region_before_instance +BACKEND_TARGETS+= backend_getnext_instance_below_region_on_instance +BACKEND_TARGETS+= backend_getnext_instance_below_region_below_instance BACKEND_TARGETS+= backend_getbulk_nonrep_zero_maxrep_one BACKEND_TARGETS+= backend_getbulk_nonrep_zero_maxrep_two BACKEND_TARGETS+= backend_getbulk_nonrep_one_maxrep_one Index: regress/usr.sbin/snmpd/backend.c === RCS file: /cvs/src/regress/usr.sbin/snmpd/backend.c,v retrieving revision 1.1 diff -u -p -r1.1 backend.c --- regress/usr.sbin/snmpd/backend.c24 Oct 2023 14:34:40 - 1.1 +++ regress/usr.sbin/snmpd/backend.c28 Oct 2023 07:57:23 - @@ -2633,6 +2633,173 @@ backend_getnext_toomany(void) } void +backend_getnext_instance_below_region_before_instance(void) +{ + struct sockaddr_storage ss; + struct sockaddr *sa = (struct sockaddr *)&ss; + socklen_t salen; + int snmp_s, ax_s; + uint32_t sessionid1, sessionid2; + struct varbind varbind[] = { + { + .type = TYPE_NULL, + .name = OID_STRUCT(MIB_BACKEND_GETNEXT, 27), + .data.int32 = 1 + }, + }; + struct searchrange searchrange[] = { + { + .start = OID_STRUCT(MIB_BACKEND_GETNEXT, 27), + .end = OID_STRUCT(MIB_BACKEND_GETNEXT, 27, 1, 0) + }, + }; + int32_t requestid; + char buf[1024]; + size_t n; + + ax_s = agentx_connect(axsocket); + sessionid1 = agentx_open(ax_s, 0, 0, + OID_ARG(MIB_SUBAGENT_BACKEND_GETNEXT, 27, 1), + "backend_getnext_instance_below_region_before_instance.1"); + sessionid2 = agentx_open(ax_s, 0, 0, + OID_ARG(MIB_SUBAGENT_BACKEND_GETNEXT, 27, 2), + "backend_getnext_instance_below_region_before_instance.2"); + agentx_register(ax_s, sessionid1, 0, 0, 127, 0, + OID_ARG(MIB_BACKEND_GETNEXT, 27), 0); + agentx_register(ax_s, sessionid2, 1, 0, 127, 0, + OID_ARG(MIB_BACKEND_GETNEXT, 27, 1, 0), 0); + + salen = sn
Re: snmpd: reject OIDS equal to searchrange.end
On Sat, 2023-10-28 at 09:45 +0200, Martijn van Duren wrote: > RFC 2741 section 5.2 states that searchrange.end is non-inclusive. > appl_varbind_valid() and appl_response() currently tests inclusive. > The appl_varbind_valid() case is for backends that support > searchrange.end (like agentx) and the appl_response() case for > those who do not and need a fixup (basically everything else). > > OK? > > martijn > Here's the regress part I forgot to add. Index: Makefile === RCS file: /cvs/src/regress/usr.sbin/snmpd/Makefile,v retrieving revision 1.6 diff -u -p -r1.6 Makefile --- Makefile27 Oct 2023 10:26:20 - 1.6 +++ Makefile28 Oct 2023 07:49:31 - @@ -170,6 +170,7 @@ BACKEND_TARGETS+= backend_getnext_stale BACKEND_TARGETS+= backend_getnext_inclusive_backwards BACKEND_TARGETS+= backend_getnext_toofew BACKEND_TARGETS+= backend_getnext_toomany +BACKEND_TARGETS+= backend_getnext_response_equal_end BACKEND_TARGETS+= backend_getbulk_nonrep_zero_maxrep_one BACKEND_TARGETS+= backend_getbulk_nonrep_zero_maxrep_two BACKEND_TARGETS+= backend_getbulk_nonrep_one_maxrep_one Index: backend.c === RCS file: /cvs/src/regress/usr.sbin/snmpd/backend.c,v retrieving revision 1.1 diff -u -p -r1.1 backend.c --- backend.c 24 Oct 2023 14:34:40 - 1.1 +++ backend.c 28 Oct 2023 07:49:31 - @@ -2633,6 +2633,62 @@ backend_getnext_toomany(void) } void +backend_getnext_response_equal_end(void) +{ + struct sockaddr_storage ss; + struct sockaddr *sa = (struct sockaddr *)&ss; + socklen_t salen; + int snmp_s, ax_s; + uint32_t sessionid1, sessionid2; + struct varbind varbind[] = { + { + .type = TYPE_NULL, + .name = OID_STRUCT(MIB_BACKEND_GETNEXT, 26), + .data.int32 = 1 + }, + }; + struct searchrange searchrange[] = { + { + .start = OID_STRUCT(MIB_BACKEND_GETNEXT, 26), + .end = OID_STRUCT(MIB_BACKEND_GETNEXT, 26, 1, 1) + }, + }; + int32_t requestid; + char buf[1024]; + size_t n; + + ax_s = agentx_connect(axsocket); + sessionid1 = agentx_open(ax_s, 0, 0, + OID_ARG(MIB_SUBAGENT_BACKEND_GETNEXT, 26, 1), + "backend_getnext_end_equal.1"); + sessionid2 = agentx_open(ax_s, 0, 0, + OID_ARG(MIB_SUBAGENT_BACKEND_GETNEXT, 26, 2), + "backend_getnext_end_equal.2"); + agentx_register(ax_s, sessionid1, 0, 0, 127, 0, + OID_ARG(MIB_BACKEND_GETNEXT, 26), 0); + agentx_register(ax_s, sessionid2, 0, 0, 127, 0, + OID_ARG(MIB_BACKEND_GETNEXT, 26, 1, 1), 0); + + salen = snmp_resolve(SOCK_DGRAM, hostname, servname, sa); + snmp_s = snmp_connect(SOCK_DGRAM, sa, salen); + requestid = snmpv2_getnext(snmp_s, community, 0, varbind, 1); + + /* Fool agentx_getnext_handle() */ + varbind[0].name.subid[varbind[0].name.n_subid++] = 1; + varbind[0].type = TYPE_INTEGER; + n = agentx_read(ax_s, buf, sizeof(buf), 1000); + agentx_getnext_handle(__func__, buf, n, 0, sessionid1, searchrange, + varbind, 1); + varbind[0].name = searchrange[0].end; + agentx_response(ax_s, buf, NOERROR, 0, varbind, 1); + varbind[0].type = TYPE_NULL; + varbind[0].name = OID_STRUCT(MIB_BACKEND_GETNEXT, 26), + + snmpv2_response_validate(snmp_s, 1000, community, requestid, GENERR, 1, + varbind, 1); +} + +void backend_getbulk_nonrep_zero_maxrep_one(void) { struct sockaddr_storage ss; Index: regress.h === RCS file: /cvs/src/regress/usr.sbin/snmpd/regress.h,v retrieving revision 1.2 diff -u -p -r1.2 regress.h --- regress.h 27 Oct 2023 10:26:20 - 1.2 +++ regress.h 28 Oct 2023 07:49:31 - @@ -293,6 +293,7 @@ void backend_getnext_stale(void); void backend_getnext_inclusive_backwards(void); void backend_getnext_toofew(void); void backend_getnext_toomany(void); +void backend_getnext_response_equal_end(void); void backend_getbulk_nonrep_zero_maxrep_one(void); void backend_getbulk_nonrep_zero_maxrep_two(void); void backend_getbulk_nonrep_one_maxrep_one(void); Index: snmpd_regress.c === RCS file: /cvs/src/regress/usr.sbin/snmpd/snmpd_regress.c,v retrieving revision 1.2 diff -u -p -r1.2 snmpd_regress.c --- snmpd_regress.c 27 Oct 2023 10:26:20 - 1.2 +++ snmpd_regress.c 28 Oct 2023 07:49:31 - @@ -143,6 +143,7 @@ const struct { { "backend_getnext_inclusive_backwards", bac
snmpd: reject OIDS equal to searchrange.end
RFC 2741 section 5.2 states that searchrange.end is non-inclusive. appl_varbind_valid() and appl_response() currently tests inclusive. The appl_varbind_valid() case is for backends that support searchrange.end (like agentx) and the appl_response() case for those who do not and need a fixup (basically everything else). OK? martijn diff --git a/application.c b/application.c index 33143d6..b3a2603 100644 --- a/application.c +++ b/application.c @@ -1100,7 +1100,7 @@ appl_response(struct appl_backend *backend, int32_t requestid, */ eomv |= !backend->ab_range && next && ober_oid_cmp(&(vb->av_oid), - &(origvb->avi_varbind.av_oid_end)) > 0; + &(origvb->avi_varbind.av_oid_end)) >= 0; /* RFC 3584 section 4.2.2.1 */ if (ureq->aru_pduversion == SNMP_V1 && vb->av_value != NULL && @@ -1283,7 +1283,7 @@ appl_varbind_valid(struct appl_varbind *varbind, } } if (range && ober_oid_cmp(&(varbind->av_oid), - &(request->avi_varbind.av_oid_end)) > 0) { + &(request->avi_varbind.av_oid_end)) >= 0) { *errstr = "end oid not honoured"; return 0; }
libagentx: don't return responses >= searchrange.end
In most cases when a region is registered we have the full ownership. As soon as a region has been registered below prior mentioned region we could loose ownership halfway through. This case currently isn't fully tested and with indices we can return OIDs >= searchrange.end. The easiest way is to test this case is in agentx_varbind_finalize() and simply reset a varbind to EOMV if we're outside our range. I've pulled apart the agentx_request_type cases for clarity and control. OK? martijn@ Index: agentx.c === RCS file: /cvs/src/lib/libagentx/agentx.c,v retrieving revision 1.23 diff -u -p -r1.23 agentx.c --- agentx.c24 Oct 2023 08:54:52 - 1.23 +++ agentx.c28 Oct 2023 07:40:59 - @@ -2822,7 +2822,7 @@ getnext: while (axo != NULL && axo->axo_cstate != AX_CSTATE_OPEN) axo = RB_NEXT(axc_objects, &(axc->axc_objects), axo); if (axo == NULL || - ax_oid_cmp(&(axo->axo_oid), &(axv->axv_end)) > 0) { + ax_oid_cmp(&(axo->axo_oid), &(axv->axv_end)) >= 0) { agentx_varbind_endofmibview(axv); return; } @@ -3349,19 +3349,53 @@ agentx_varbind_finalize(struct agentx_va #endif } } - cmp = ax_oid_cmp(&(axv->axv_vb.avb_oid), &oid); - if ((agentx_varbind_request(axv) == AGENTX_REQUEST_TYPE_GETNEXT && - cmp >= 0) || cmp > 0) { + cmp = ax_oid_cmp(&oid, &(axv->axv_vb.avb_oid)); + switch (agentx_varbind_request(axv)) { + case AGENTX_REQUEST_TYPE_GET: + if (cmp != 0) { #ifdef AX_DEBUG - agentx_log_axg_fatalx(axg, "indices not incremented"); + agentx_log_axg_fatalx(axg, "index changed"); #else - agentx_log_axg_warnx(axg, "indices not incremented"); - bcopy(&(axv->axv_start), &(axv->axv_vb.avb_oid), - sizeof(axv->axv_start)); - axv->axv_error = AX_PDU_ERROR_GENERR; + agentx_log_axg_warnx(axg, "index changed"); + bcopy(&(axv->axv_start), &(axv->axv_vb.avb_oid), + sizeof(axv->axv_start)); + axv->axv_error = AX_PDU_ERROR_GENERR; + break; #endif - } else + } + break; + case AGENTX_REQUEST_TYPE_GETNEXT: + if (cmp <= 0) { +#ifdef AX_DEBUG + agentx_log_axg_fatalx(axg, "indices not incremented"); +#else + agentx_log_axg_warnx(axg, "indices not incremented"); + bcopy(&(axv->axv_start), &(axv->axv_vb.avb_oid), + sizeof(axv->axv_start)); + axv->axv_error = AX_PDU_ERROR_GENERR; + break; +#endif + } + /* FALLTHROUGH */ + case AGENTX_REQUEST_TYPE_GETNEXTINCLUSIVE: + if (cmp < 0) { +#ifdef AX_DEBUG + agentx_log_axg_fatalx(axg, "index decremented"); +#else + agentx_log_axg_warnx(axg, "index decremented"); + bcopy(&(axv->axv_start), &(axv->axv_vb.avb_oid), + sizeof(axv->axv_start)); + axv->axv_error = AX_PDU_ERROR_GENERR; + break; +#endif + } + if (axv->axv_end.aoi_idlen != 0 && + ax_oid_cmp(&oid, &(axv->axv_end)) >= 0) { + agentx_varbind_endofmibview(axv); + return; + } bcopy(&oid, &(axv->axv_vb.avb_oid), sizeof(oid)); + } done: agentx_object_unlock(axv->axv_axo); agentx_get_finalize(axv->axv_axg);
Re: snmpd; Fix use after free for appl_request_upstream
On Thu, 2023-10-26 at 21:38 +0200, Theo Buehler wrote: > On Thu, Oct 26, 2023 at 11:51:00AM +0200, Martijn van Duren wrote: > > This case is covered by the new regress' backend_get_toofew and > > backend_get_toomany tests. However, even with MALLOC_OPTIONS cranked > > to the max it's really hard to trigger (I had to run > > backend_get_wrongorder, backend_get_toofew, backend_get_toomany > > sequentially in a tight loop killing snmpd between iterations for the > > best chance). > > > > When we receive an invalid varbindlist in a response we set the invalid > > variable. This in turn calls appl_varbind_error(), but the avi_state > > of the varbinds remains in APPL_VBSTATE_PENDING. Directly following > > we call appl_request_downstream_free(), which sees that the varbinds > > haven't been resolved, triggering a call to > > appl_request_upstream_resolve(). This call in turn sees that the > > error has been set and just sends out the error-response to the client > > and frees the appl_request_upstream. From here we return back to > > appl_response(), which also calls appl_request_upstream_resolve(), > > resulting in a use after free. > > > > The main tool for fixing this issue is making use of > > appl_request_upstream's aru_locked member, which will cause > > appl_request_upstream_resolve() to return instantly. The simplest fix is > > to set aru_locked before calling appl_request_downstream_free() and > > unsetting it directly afterwards inside appl_response(). > > > > The second one is the diff proposed below, which shrinks the code. > > > > appl_request_upstream_free() is only called once from > > appl_request_upstream_reply(). appl_request_upstream_reply() in turn > > is only called by appl_request_upstream_resolve(). > > appl_request_upstream_resolve() is called in 3 places: > > - appl_processpdu(): to kick things off > > - appl_request_downstream_free(): For when a backend disappears with > > outstanding requests > > - appl_response(): To kickstart the next round of resolving. > > > > Since appl_request_downstream_free() is always called from > > appl_response(), we can leverage that function and make it call > > appl_request_upstream_resolve() unconditionally. > > > > appl_request_downstream_free() is called from the following locations: > > - appl_close(): When a backend has disappeared. > > - appl_request_upstream_free(): We send out a reply early, because an > > error has been detected. > > - appl_response(): We received a response > > > > appl_request_upstream_free() can't reenter into > > appl_request_upstream_resolve(), or it would potentially trigger new > > appl_request_downstreams. This can be prevented by setting aru_locked > > before calling appl_request_downstream_free(). > > For all other cases we should rely on appl_request_upstream_resolve()'s > > logic to handle varbinds in any state, so there's no reason make calls > > from other contexts conditional. > > Your description of the bug makes sense and your choice of resolving it > as well. Thanks for the in-depth explanation, that helped a lot. Still, > I must say that I don't really feel at ease with the amount of complexity > and entanglement here. I simply can't fit all of this into my head within > a reasonable amount of time. > Well, the S in SNMP doesn't stand for "I want to pull my hair out" for no reason. So for people interested, this is the high-over flow of the code: When a message is received snmpe.c sends the PDU part to appl_processpdu(). This function pulls apart the request in structs that can be worked with internally (1 struct appl_request_upstream + n struct appl_varbind_internal). After things are pulled apart appl_processpdu() calls appl_request_upstream_resolve(), which is responsible for retrieving the values for the requested varbinds. It does this by going over all varbinds in the APPL_VBSTATE_NEW state, and calling appl_varbind_backend() to find a matching backend for the request. In the case of a getnext/getbulk request it can also increment the oid to the next matching region (if needed) and it also fills the end oid where the ownership of the backend ends. After all APPL_VBSTATE_NEW varbinds have found their backend, they are grouped together by backend in a struct appl_request_downstream and send out via appl_request_downstream_send(). Once a backend has a response available it calls appl_response(). This function verifies the returned data via appl_varbind_valid() and appl_error_valid(), and checks if the data matches the supported datatypes (e.g. counter64 on SNMPv1). After that the data is put
snmpd; Fix use after free for appl_request_upstream
This case is covered by the new regress' backend_get_toofew and backend_get_toomany tests. However, even with MALLOC_OPTIONS cranked to the max it's really hard to trigger (I had to run backend_get_wrongorder, backend_get_toofew, backend_get_toomany sequentially in a tight loop killing snmpd between iterations for the best chance). When we receive an invalid varbindlist in a response we set the invalid variable. This in turn calls appl_varbind_error(), but the avi_state of the varbinds remains in APPL_VBSTATE_PENDING. Directly following we call appl_request_downstream_free(), which sees that the varbinds haven't been resolved, triggering a call to appl_request_upstream_resolve(). This call in turn sees that the error has been set and just sends out the error-response to the client and frees the appl_request_upstream. From here we return back to appl_response(), which also calls appl_request_upstream_resolve(), resulting in a use after free. The main tool for fixing this issue is making use of appl_request_upstream's aru_locked member, which will cause appl_request_upstream_resolve() to return instantly. The simplest fix is to set aru_locked before calling appl_request_downstream_free() and unsetting it directly afterwards inside appl_response(). The second one is the diff proposed below, which shrinks the code. appl_request_upstream_free() is only called once from appl_request_upstream_reply(). appl_request_upstream_reply() in turn is only called by appl_request_upstream_resolve(). appl_request_upstream_resolve() is called in 3 places: - appl_processpdu(): to kick things off - appl_request_downstream_free(): For when a backend disappears with outstanding requests - appl_response(): To kickstart the next round of resolving. Since appl_request_downstream_free() is always called from appl_response(), we can leverage that function and make it call appl_request_upstream_resolve() unconditionally. appl_request_downstream_free() is called from the following locations: - appl_close(): When a backend has disappeared. - appl_request_upstream_free(): We send out a reply early, because an error has been detected. - appl_response(): We received a response appl_request_upstream_free() can't reenter into appl_request_upstream_resolve(), or it would potentially trigger new appl_request_downstreams. This can be prevented by setting aru_locked before calling appl_request_downstream_free(). For all other cases we should rely on appl_request_upstream_resolve()'s logic to handle varbinds in any state, so there's no reason make calls from other contexts conditional. OK? martijn@ Index: application.c === RCS file: /cvs/src/usr.sbin/snmpd/application.c,v retrieving revision 1.24 diff -u -p -r1.24 application.c --- application.c 24 Oct 2023 14:21:58 - 1.24 +++ application.c 26 Oct 2023 09:40:23 - @@ -710,6 +710,7 @@ appl_request_upstream_free(struct appl_r if (ureq == NULL) return; + ureq->aru_locked = 1; for (i = 0; i < ureq->aru_varbindlen && ureq->aru_vblist != NULL; i++) { vb = &(ureq->aru_vblist[i]); ober_free_elements(vb->avi_varbind.av_value); @@ -726,7 +727,6 @@ void appl_request_downstream_free(struct appl_request_downstream *dreq) { struct appl_varbind_internal *vb; - int retry = 0; if (dreq == NULL) return; @@ -736,14 +736,11 @@ appl_request_downstream_free(struct appl for (vb = dreq->ard_vblist; vb != NULL; vb = vb->avi_next) { vb->avi_request_downstream = NULL; - if (vb->avi_state == APPL_VBSTATE_PENDING) { + if (vb->avi_state == APPL_VBSTATE_PENDING) vb->avi_state = APPL_VBSTATE_NEW; - retry = 1; - } } - if (retry) - appl_request_upstream_resolve(dreq->ard_request); + appl_request_upstream_resolve(dreq->ard_request); free(dreq); } @@ -1172,9 +1169,6 @@ appl_response(struct appl_backend *backe backend->ab_name); backend->ab_fn->ab_close(backend, APPL_CLOSE_REASONPARSEERROR); } - - if (ureq != NULL) - appl_request_upstream_resolve(ureq); } int
snmpd: Fix close after protocol error case
So here's an elusive one that can be triggered every now and then by the new regression test. Once an AgentX session is opened and we send an invalid packet appl_agentx_recv() goes to appl_agentx_free(), since there's no recovery. appl_agentx_free() tries to neatly close all open sessions by sending a close-pdu, followed by calling appl_agentx_send() directly. However, if the socket has been closed in the meantime we hit appl_agentx_send()'s error path, which also calls appl_agentx_free(). This in turn leads to use after free cases. To fix this don't call appl_agentx_send() directly anymore, but just schedule it via conn_wev. To make sure as much data as possible is written out do a last unchecked courtesy flush before definitively freeing the connection. Since appl_agentx_forceclose() arms conn_wev move the event_del() calls down in appl_agentx_free(). Other calls of appl_agentx_send() should be fine, but just convert all of them to be consistent and safe. OK? martijn@ Index: usr.sbin/snmpd/application_agentx.c === RCS file: /cvs/src/usr.sbin/snmpd/application_agentx.c,v retrieving revision 1.12 diff -u -p -r1.12 application_agentx.c --- usr.sbin/snmpd/application_agentx.c 24 Oct 2023 14:11:14 - 1.12 +++ usr.sbin/snmpd/application_agentx.c 26 Oct 2023 08:43:02 - @@ -254,9 +254,6 @@ appl_agentx_free(struct appl_agentx_conn { struct appl_agentx_session *session; - event_del(&(conn->conn_rev)); - event_del(&(conn->conn_wev)); - while ((session = TAILQ_FIRST(&(conn->conn_sessions))) != NULL) { if (conn->conn_ax == NULL) appl_agentx_session_free(session); @@ -265,7 +262,12 @@ appl_agentx_free(struct appl_agentx_conn reason); } + event_del(&(conn->conn_rev)); + event_del(&(conn->conn_wev)); + RB_REMOVE(appl_agentx_conns, &appl_agentx_conns, conn); + if (conn->conn_ax != NULL) + (void)ax_send(conn->conn_ax); ax_free(conn->conn_ax); if (conn->conn_backend) fatalx("AgentX(%"PRIu32"): disappeared unexpected", @@ -419,7 +421,7 @@ appl_agentx_recv(int fd, short event, vo pdu->ap_header.aph_transactionid, pdu->ap_header.aph_packetid, smi_getticks(), APPL_ERROR_NOERROR, 0, NULL, 0); - appl_agentx_send(-1, EV_WRITE, conn); + event_add(&(conn->conn_wev), NULL); break; case AX_PDU_TYPE_INDEXALLOCATE: case AX_PDU_TYPE_INDEXDEALLOCATE: @@ -431,7 +433,7 @@ appl_agentx_recv(int fd, short event, vo APPL_ERROR_PROCESSINGERROR, 1, pdu->ap_payload.ap_vbl.ap_varbind, pdu->ap_payload.ap_vbl.ap_nvarbind); - appl_agentx_send(-1, EV_WRITE, conn); + event_add(&(conn->conn_wev), NULL); break; case AX_PDU_TYPE_ADDAGENTCAPS: case AX_PDU_TYPE_REMOVEAGENTCAPS: @@ -451,7 +453,7 @@ appl_agentx_recv(int fd, short event, vo pdu->ap_header.aph_transactionid, pdu->ap_header.aph_packetid, smi_getticks(), error, 0, NULL, 0); - appl_agentx_send(-1, EV_WRITE, conn); + event_add(&(conn->conn_wev), NULL); ax_pdu_free(pdu); if (session == NULL || error != APPL_ERROR_PARSEERROR) @@ -560,13 +562,13 @@ appl_agentx_open(struct appl_agentx_conn ax_response(conn->conn_ax, session->sess_id, pdu->ap_header.aph_transactionid, pdu->ap_header.aph_packetid, smi_getticks(), APPL_ERROR_NOERROR, 0, NULL, 0); - appl_agentx_send(-1, EV_WRITE, conn); + event_add(&(conn->conn_wev), NULL); return; fail: ax_response(conn->conn_ax, 0, pdu->ap_header.aph_transactionid, pdu->ap_header.aph_packetid, 0, error, 0, NULL, 0); - appl_agentx_send(-1, EV_WRITE, conn); + event_add(&(conn->conn_wev), NULL); if (session != NULL) free(session->sess_descr.aos_string); free(session); @@ -592,7 +594,7 @@ appl_agentx_close(struct appl_agentx_ses ax_response(conn->conn_ax, pdu->ap_header.aph_sessionid, pdu->ap_header.aph_transactionid, pdu->ap_header.aph_packetid, smi_getticks(), error, 0, NULL, 0); - appl_agentx_send(-1, EV_WRITE, conn); + event_add(&(conn->conn_wev), NULL); if (error == APPL_ERROR_NOERROR) return; @@ -612,7 +614,7 @@ appl_agentx_forceclose(struct appl_backe session->sess_conn->conn_ax->ax_byteorder = session->sess_byteorder; ax_close(session->sess_conn->conn_ax, session->sess_id, (enum ax_close_reason) reason); - appl_agentx_send(-1, EV_WRITE, session->sess_conn); + event_add(&(session->sess_conn->conn_wev), NULL); strlcpy(name, session->sess_backend.ab_name, sizeof(name));
snmpd: remove filter-pf-addresses support
OpenBSD 7.4 is here. upgrade72.html already mentions it's deprecation. OK? martijn@ Index: parse.y === RCS file: /cvs/src/usr.sbin/snmpd/parse.y,v retrieving revision 1.78 diff -u -p -r1.78 parse.y --- parse.y 6 Oct 2022 14:41:08 - 1.78 +++ parse.y 19 Oct 2023 14:12:55 - @@ -140,7 +140,7 @@ typedef struct { %token SYSTEM CONTACT DESCR LOCATION NAME OBJECTID SERVICES RTFILTER %token READONLY READWRITE OCTETSTRING INTEGER COMMUNITY TRAP RECEIVER %token SECLEVEL NONE AUTH ENC USER AUTHKEY ENCKEY ERROR -%token HANDLE DEFAULT SRCADDR TCP UDP PFADDRFILTER BLOCKLIST PORT +%token HANDLE DEFAULT SRCADDR TCP UDP BLOCKLIST PORT %token STRING %token NUMBER %typeusmuser community optcommunity @@ -336,26 +336,6 @@ main : LISTEN ON listen_udptcp else conf->sc_rtfilter = 0; } - /* XXX Remove after 7.4 */ - | PFADDRFILTER yesno{ - struct ber_oid *blocklist; - - log_warnx("filter-pf-addresses is deprecated. " - "Please use blocklist pfTblAddrTable instead."); - if ($2) { - blocklist = recallocarray(conf->sc_blocklist, - conf->sc_nblocklist, - conf->sc_nblocklist + 1, - sizeof(*blocklist)); - if (blocklist == NULL) { - yyerror("malloc"); - YYERROR; - } - conf->sc_blocklist = blocklist; - smi_string2oid("pfTblAddrTable", - &(blocklist[conf->sc_nblocklist++])); - } - } | seclevel { conf->sc_min_seclevel = $1; } @@ -1195,7 +1175,6 @@ lookup(char *s) { "enc",ENC }, { "enckey", ENCKEY }, { "engineid", ENGINEID }, - { "filter-pf-addresses",PFADDRFILTER }, { "filter-routes", RTFILTER }, { "group", GROUP }, { "handle", HANDLE },
snmpd_metrics: differentiate between hrSWRunName and hrSWRunPath
Right now we return the same value for both hrSWRunName and hrSWRunPath. hrSWRunPath should return the full path of the binary, and hrSWRunName a description of the running software. Afaik there's no proper way to retrieve the full path of the running binary, However, in a lot of cases argv[0] can contain the full or relative path. But if argv[0] gets overwritten (like most of our daemons' children) it gives a more descriptive name, which is more in line with hrSWRunName. netsnmp's snmpd uses argv[0] for hrSWRunPath and kinfo_proc's p_comm for hrSWRunName, and snmptop defaults to hrSWRunName. top(1) also defaults to p_comm, but contrary to top(1), snmptop allows us to switch between hrSWRunName, and hrSWRunPath and toggling of hrSWRunParameters independently, where top(1) toggles between p_comm and argv[]. So there's an argument to be made either way, but for this diff I stuck with netsnmp's choices. While here, change the buffer length from 128 to 129. HOST-RESOURCES-MIB allows up to 128 characters in the response, so make room for the terminating NUL. Thoughts? OK? martijn@ Index: mib.c === RCS file: /cvs/src/libexec/snmpd/snmpd_metrics/mib.c,v retrieving revision 1.4 diff -u -p -r1.4 mib.c --- mib.c 4 Jul 2023 11:34:19 - 1.4 +++ mib.c 18 Oct 2023 11:47:00 - @@ -103,7 +103,9 @@ int kinfo_proc_comp(const void *, const int kinfo_proc(u_int32_t, struct kinfo_proc **); voidkinfo_timer_cb(int, short, void *); voidkinfo_proc_free(void); -int kinfo_args(struct kinfo_proc *, char **); +int kinfo_args(struct kinfo_proc *, char ***); +int kinfo_path(struct kinfo_proc *, char **); +int kinfo_parameters(struct kinfo_proc *, char **); /* IF-MIB */ struct agentx_index *ifIdx; @@ -669,13 +671,21 @@ mib_hrswrun(struct agentx_varbind *vb) if (obj == hrSWRunIndex) agentx_varbind_integer(vb, kinfo->p_pid); - else if (obj == hrSWRunName || obj == hrSWRunPath) + else if (obj == hrSWRunName) agentx_varbind_string(vb, kinfo->p_comm); - else if (obj == hrSWRunID) + else if (obj == hrSWRunPath) { + if (kinfo_path(kinfo, &s) == -1) { + log_warn("kinfo_path"); + agentx_varbind_error(vb); + return; + } + + agentx_varbind_string(vb, s); + } else if (obj == hrSWRunID) agentx_varbind_oid(vb, AGENTX_OID(0, 0)); else if (obj == hrSWRunParameters) { - if (kinfo_args(kinfo, &s) == -1) { - log_warn("kinfo_args"); + if (kinfo_parameters(kinfo, &s) == -1) { + log_warn("kinfo_parameters"); agentx_varbind_error(vb); return; } @@ -811,25 +821,22 @@ kinfo_proc_free(void) } int -kinfo_args(struct kinfo_proc *kinfo, char **s) +kinfo_args(struct kinfo_proc *kinfo, char ***s) { - static char str[128]; static char *buf = NULL; static size_tbuflen = 128; int mib[] = { CTL_KERN, KERN_PROC_ARGS, kinfo->p_pid, KERN_PROC_ARGV }; - char*nbuf, **argv; + char*nbuf; + *s = NULL; if (buf == NULL) { buf = malloc(buflen); if (buf == NULL) return (-1); } - str[0] = '\0'; - *s = str; - while (sysctl(mib, nitems(mib), buf, &buflen, NULL, 0) == -1) { if (errno != ENOMEM) { /* some errors are expected, dont get too upset */ @@ -844,11 +851,41 @@ kinfo_args(struct kinfo_proc *kinfo, cha buflen += 128; } - argv = (char **)buf; - if (argv[0] == NULL) - return (0); + *s = (char **)buf; + return (0); +} + +int +kinfo_path(struct kinfo_proc *kinfo, char **s) +{ + static char str[129]; + char**argv; + + if (kinfo_args(kinfo, &argv) == -1) + return (-1); + str[0] = '\0'; + *s = str; + if (argv != NULL && argv[0] != NULL) + strlcpy(str, argv[0], sizeof(str)); + return (0); +} + +int +kinfo_parameters(struct kinfo_proc *kinfo, char **s) +{ + static char str[129]; + char**argv; + + if (kinfo_args(kinfo, &argv) == -1) + return (-1); + + str[0] = '\0'; + *s = str; + if (argv == NULL || argv[0] == NULL) + return (0); argv++; + while (*argv != NULL) { strlcat(str, *argv, sizeof(str)); argv++;
snmpd_metrics: add HOST-RESOURCES-MIB:hrSWRunPerfTable
This diff adds the two entries from the hrSWRunPerfTable: hrSWRunPerfCPU, and hrSWRunPerfMem. This allows snmptop from the net-snmp package to work. Math stolen^Wcopied from top/machine.c. OK? martijn@ Index: mib.c === RCS file: /cvs/src/libexec/snmpd/snmpd_metrics/mib.c,v retrieving revision 1.4 diff -u -p -r1.4 mib.c --- mib.c 4 Jul 2023 11:34:19 - 1.4 +++ mib.c 18 Oct 2023 09:52:07 - @@ -69,6 +69,10 @@ struct event connev; const char *agentxsocket = NULL; int agentxfd = -1; +int pageshift; +#define pagetok(size) ((size) << pageshift) + +voidpageshift_init(void); voidsnmp_connect(struct agentx *, void *, int); voidsnmp_tryconnect(int, short, void *); voidsnmp_read(int, short, void *); @@ -89,6 +93,7 @@ struct agentx_object *hrProcessorLoad; struct agentx_index *hrSWRunIdx; struct agentx_object *hrSWRunIndex, *hrSWRunName, *hrSWRunID, *hrSWRunPath; struct agentx_object *hrSWRunParameters, *hrSWRunType, *hrSWRunStatus; +struct agentx_object *hrSWRunPerfCPU, *hrSWRunPerfMem; voidmib_hrsystemuptime(struct agentx_varbind *); voidmib_hrsystemdate(struct agentx_varbind *); @@ -634,6 +639,7 @@ mib_hrswrun(struct agentx_varbind *vb) struct agentx_object*obj; enum agentx_request_type req; int32_t idx; + int32_t time; struct kinfo_proc *kinfo; char*s; @@ -711,6 +717,13 @@ mib_hrswrun(struct agentx_varbind *vb) agentx_varbind_integer(vb, 4); break; } + } else if (obj == hrSWRunPerfCPU) { + time = kinfo->p_rtime_sec * 100; + time += (kinfo->p_rtime_usec + 5000) / 1; + agentx_varbind_integer(vb, time); + } else if (obj == hrSWRunPerfMem) { + agentx_varbind_integer(vb, pagetok(kinfo->p_vm_tsize + + kinfo->p_vm_dsize + kinfo->p_vm_ssize)); } else fatal("%s: Unexpected object", __func__); } @@ -3278,6 +3291,7 @@ main(int argc, char *argv[]) kr_init(); pf_init(); timer_init(); + pageshift_init(); if (agentxsocket != NULL) { if (strlcpy(agentxsocketdir, agentxsocket, @@ -3375,6 +3389,10 @@ main(int argc, char *argv[]) (hrSWRunType = agentx_object(host, AGENTX_OID(HRSWRUNTYPE), &hrSWRunIdx, 1, 0, mib_hrswrun)) == NULL || (hrSWRunStatus = agentx_object(host, AGENTX_OID(HRSWRUNSTATUS), + &hrSWRunIdx, 1, 0, mib_hrswrun)) == NULL || + (hrSWRunPerfCPU = agentx_object(host, AGENTX_OID(HRSWRUNPERFCPU), + &hrSWRunIdx, 1, 0, mib_hrswrun)) == NULL || + (hrSWRunPerfMem = agentx_object(host, AGENTX_OID(HRSWRUNPERFMEM), &hrSWRunIdx, 1, 0, mib_hrswrun)) == NULL) fatal("agentx_object"); @@ -4318,6 +4336,22 @@ main(int argc, char *argv[]) log_setverbose(verbose); event_dispatch(); +} + +#define LOG1024 10 +void +pageshift_init(void) +{ + long pagesize; + + if ((pagesize = sysconf(_SC_PAGESIZE)) == -1) + fatal("sysconf(_SC_PAGESIZE)"); + while (pagesize > 1) { + pageshift++; + pagesize >>= 1; + } + /* we only need the amount of log(2)1024 for our conversion */ + pageshift -= LOG1024; } void
snmpd [16/16]: Keep track of exact registrations via AgentX
This diff I'm least convinced about, but I do want to put it out there. As far as I'm aware RFC2741 places no restrictions on direct mapping of unregistrations on registrations. Meaning that if I register 1.3.6.1.2.[1-10] and I just unregister 1.3.6.1.2.5 this should leave 1.3.6.1.2.[1-4] and 1.3.6.1.2.[6-10] and this is what currently is implemented (assuming previous (un)register diffs). However, RFC2742 specifies AgentxRegistrationEntry, which presents the the data as presented in the agentx-register-pdu. This implies that an unregister is expected to be an exact counterpart to agentx-register-pdu, because else an AgentxRegistrationEntry would need to be split up, which would be a horror-show. Do we want to add this restriction and keep the road open to AGENTX-MIB support? Since range support is currently broken anyway this won't cause any problems. Thoughts? OK? martijn@ diff --git a/application_agentx.c b/application_agentx.c index 79900d6..60dc4df 100644 --- a/application_agentx.c +++ b/application_agentx.c @@ -70,11 +70,28 @@ struct appl_agentx_session { struct ax_oid sess_oid; struct ax_ostring sess_descr; struct appl_backend sess_backend; + TAILQ_HEAD(, appl_agentx_registration) sess_registrations; RB_ENTRY(appl_agentx_session) sess_entry; TAILQ_ENTRY(appl_agentx_session) sess_conn_entry; }; +/* RFC2742: AGENTX-MIB:AgentxRegistrationEntry */ +struct appl_agentx_registration { + uint32_t reg_index; + char reg_context[APPL_CONTEXTNAME_MAX + 1]; + struct ax_oid reg_start; + uint8_t reg_rangesubid; + uint32_t reg_upperbound; + uint8_t reg_priority; + uint8_t reg_timeout; + uint8_t reg_instance; + + struct appl_agentx_session *reg_session; + + TAILQ_ENTRY(appl_agentx_registration) reg_entry; +}; + void appl_agentx_listen(struct agentx_master *); void appl_agentx_accept(int, short, void *); void appl_agentx_free(struct appl_agentx_connection *, enum appl_close_reason); @@ -106,6 +123,7 @@ struct appl_backend_functions appl_agentx_functions = { .ab_getnext = appl_agentx_getnext, .ab_getbulk = NULL, /* not properly supported in application.c and libagentx */ }; +static uint32_t appl_agentx_reg_index = 1; RB_HEAD(appl_agentx_conns, appl_agentx_connection) appl_agentx_conns = RB_INITIALIZER(&appl_agentx_conns); @@ -477,6 +495,7 @@ appl_agentx_open(struct appl_agentx_connection *conn, struct ax_pdu *pdu) goto fail; } session->sess_descr.aos_string = NULL; + TAILQ_INIT(&(session->sess_registrations)); session->sess_conn = conn; if (pdu->ap_header.aph_flags & AX_PDU_FLAG_NETWORK_BYTE_ORDER) @@ -624,6 +643,12 @@ void appl_agentx_session_free(struct appl_agentx_session *session) { struct appl_agentx_connection *conn = session->sess_conn; + struct appl_agentx_registration *reg; + + while ((reg = TAILQ_FIRST(&(session->sess_registrations))) != NULL) { + TAILQ_REMOVE(&(session->sess_registrations), reg, reg_entry); + free(reg); + } appl_close(&(session->sess_backend)); @@ -642,6 +667,7 @@ appl_agentx_register(struct appl_agentx_session *session, struct ax_pdu *pdu) struct ber_oid oid; enum appl_error error; int subtree = 0; + struct appl_agentx_registration *reg; timeout = pdu->ap_payload.ap_register.ap_timeout; timeout = timeout != 0 ? timeout : session->sess_timeout != 0 ? @@ -660,6 +686,13 @@ appl_agentx_register(struct appl_agentx_session *session, struct ax_pdu *pdu) goto fail; } + if ((reg = malloc(sizeof(*reg))) == NULL) { + log_warn("%s: Failed to register", + session->sess_backend.ab_name); + error = APPL_ERROR_PROCESSINGERROR; + goto fail; + } + error = appl_register(pdu->ap_context.aos_string, timeout, pdu->ap_payload.ap_register.ap_priority, &oid, pdu->ap_header.aph_flags & AX_PDU_FLAG_INSTANCE_REGISTRATION, @@ -667,6 +700,26 @@ appl_agentx_register(struct appl_agentx_session *session, struct ax_pdu *pdu) pdu->ap_payload.ap_register.ap_upper_bound, &(session->sess_backend)); + if (error == APPL_ERROR_NOERROR) { + reg->reg_index = appl_agentx_reg_index++; + (void)strlcpy(reg->reg_context, pdu->ap_context.aos_slen == 0 ? + "" : (const char *)pdu->ap_context.aos_string, + sizeof(reg->reg_context)); + reg->reg_start = pdu->ap_payload.ap_register.ap_subtree; + reg->reg_rangesubid = + pdu->ap_payload.ap_register.ap_range_subid; + reg->reg_upperbound = + pdu->ap_payload.ap_register.ap_upper_bound; + reg->reg_priority = pdu->ap_payload.ap_register.ap_priority; + reg->r
snmpd [15/16]: When we have an error, all oids must be identical to the request
RFC3416 section 4.2.1 (and others) tells us that if an error occurs the varbindlist in the response must be identical to the original request. There might be other edge-cases here, but let's at least make sure that the OIDs haven't been tampered with. OK? martijn@ diff --git a/application.c b/application.c index 53e793d..d98918f 100644 --- a/application.c +++ b/application.c @@ -1186,6 +1186,9 @@ appl_varbind_valid(struct appl_varbind *varbind, int cmp; int eomv = 0; + if (null) + next = 0; + if (varbind->av_value == NULL) { if (!null) { *errstr = "missing value";
snmpd [14/16]: Validate the returned error code
Certain error codes are only intended for certain request-types. Add an appl_error_valid() function to test for this. OK? martijn@ diff --git a/application.c b/application.c index 6ddeb39..53e793d 100644 --- a/application.c +++ b/application.c @@ -130,6 +130,7 @@ void appl_request_downstream_timeout(int, short, void *); void appl_request_upstream_reply(struct appl_request_upstream *); int appl_varbind_valid(struct appl_varbind *, struct appl_varbind_internal *, int, int, int, const char **); +int appl_error_valid(enum appl_error, enum snmp_pdutype); int appl_varbind_backend(struct appl_varbind_internal *); void appl_varbind_error(struct appl_varbind_internal *, enum appl_error); void appl_report(struct snmp_message *, int32_t, struct ber_oid *, @@ -1069,6 +1070,11 @@ appl_response(struct appl_backend *backend, int32_t requestid, next = pdutype == SNMP_C_GETNEXTREQ || pdutype == SNMP_C_GETBULKREQ; origvb = dreq->ard_vblist; + if (!appl_error_valid(error, dreq->ard_requesttype)) { + log_warnx("%s: %"PRIu32" Invalid error", + backend->ab_name, requestid); + invalid = 1; + } } vb = vblist; @@ -1295,6 +1301,37 @@ appl_varbind_valid(struct appl_varbind *varbind, return 1; } +int +appl_error_valid(enum appl_error error, enum snmp_pdutype type) +{ + switch (error) { + case APPL_ERROR_NOERROR: + case APPL_ERROR_TOOBIG: + case APPL_ERROR_NOSUCHNAME: + case APPL_ERROR_GENERR: + return 1; + case APPL_ERROR_BADVALUE: + case APPL_ERROR_READONLY: + case APPL_ERROR_NOACCESS: + case APPL_ERROR_WRONGTYPE: + case APPL_ERROR_WRONGLENGTH: + case APPL_ERROR_WRONGENCODING: + case APPL_ERROR_WRONGVALUE: + case APPL_ERROR_NOCREATION: + case APPL_ERROR_INCONSISTENTVALUE: + case APPL_ERROR_RESOURCEUNAVAILABLE: + case APPL_ERROR_COMMITFAILED: + case APPL_ERROR_UNDOFAILED: + case APPL_ERROR_NOTWRITABLE: + case APPL_ERROR_INCONSISTENTNAME: + return type == SNMP_C_SETREQ; + case APPL_ERROR_AUTHORIZATIONERROR: + return type == SNMP_C_GETREQ || type == SNMP_C_SETREQ; + default: + return 0; + } +} + int appl_varbind_backend(struct appl_varbind_internal *ivb) {
snmpd [13/16]: registered instances must not return below OID
If a backend registers as an instance it must never return OIDs below their registration. Add a test for this in appl_varbind_valid(). OK? martijn@ diff --git a/application.c b/application.c index dfa7220..6ddeb39 100644 --- a/application.c +++ b/application.c @@ -128,8 +128,8 @@ void appl_request_upstream_resolve(struct appl_request_upstream *); void appl_request_downstream_send(struct appl_request_downstream *); void appl_request_downstream_timeout(int, short, void *); void appl_request_upstream_reply(struct appl_request_upstream *); -int appl_varbind_valid(struct appl_varbind *, struct appl_varbind *, int, int, -int, const char **); +int appl_varbind_valid(struct appl_varbind *, struct appl_varbind_internal *, +int, int, int, const char **); int appl_varbind_backend(struct appl_varbind_internal *); void appl_varbind_error(struct appl_varbind_internal *, enum appl_error); void appl_report(struct snmp_message *, int32_t, struct ber_oid *, @@ -1073,9 +1073,9 @@ appl_response(struct appl_backend *backend, int32_t requestid, vb = vblist; for (i = 1; vb != NULL; vb = vb->av_next, i++) { -if (!appl_varbind_valid(vb, origvb == NULL ? - NULL : &(origvb->avi_varbind), next, -error != APPL_ERROR_NOERROR, backend->ab_range, &errstr)) { +if (!appl_varbind_valid(vb, origvb == NULL ? NULL : origvb, + next, error != APPL_ERROR_NOERROR, backend->ab_range, + &errstr)) { smi_oid2string(&(vb->av_oid), oidbuf, sizeof(oidbuf), 0); log_warnx("%s: %"PRIu32" %s: %s", @@ -1173,8 +1173,9 @@ appl_response(struct appl_backend *backend, int32_t requestid, } int -appl_varbind_valid(struct appl_varbind *varbind, struct appl_varbind *request, -int next, int null, int range, const char **errstr) +appl_varbind_valid(struct appl_varbind *varbind, +struct appl_varbind_internal *request, int next, int null, int range, +const char **errstr) { int cmp; int eomv = 0; @@ -1259,24 +1260,32 @@ appl_varbind_valid(struct appl_varbind *varbind, struct appl_varbind *request, if (request == NULL) return 1; - cmp = ober_oid_cmp(&(request->av_oid), &(varbind->av_oid)); - if (next && !eomv) { - if (request->av_include) { - if (cmp > 0) { - *errstr = "oid not incrementing"; - return 0; + cmp = ober_oid_cmp(&(request->avi_varbind.av_oid), &(varbind->av_oid)); + if (next) { + if (request->avi_region->ar_instance && + ober_oid_cmp(&(request->avi_region->ar_oid), + &(varbind->av_oid)) != 0) { + *errstr = "oid below instance"; + return 0; + } + if (!eomv) { + if (request->avi_varbind.av_include) { + if (cmp > 0) { + *errstr = "oid not incrementing"; + return 0; + } + } else { + if (cmp >= 0) { + *errstr = "oid not incrementing"; + return 0; + } } - } else { - if (cmp >= 0) { - *errstr = "oid not incrementing"; + if (range && ober_oid_cmp(&(varbind->av_oid), + &(request->avi_varbind.av_oid_end)) > 0) { + *errstr = "end oid not honoured"; return 0; } } - if (range && ober_oid_cmp(&(varbind->av_oid), - &(request->av_oid_end)) > 0) { - *errstr = "end oid not honoured"; - return 0; - } } else { if (cmp != 0) { *errstr = "oids not equal";
snmpd [12/16]: Make ab_range in application_agentx explicit 1
appl_agentx_session doesn't set ab_range explicitly to 1, and thus relies on malloc randomness to set it. Sit it explicitly. OK? martijn@ diff --git a/application_agentx.c b/application_agentx.c index 680725d..79900d6 100644 --- a/application_agentx.c +++ b/application_agentx.c @@ -548,6 +548,7 @@ appl_agentx_open(struct appl_agentx_connection *conn, struct ax_pdu *pdu) session->sess_backend.ab_cookie = session; session->sess_backend.ab_retries = 0; session->sess_backend.ab_fn = &appl_agentx_functions; + session->sess_backend.ab_range = 1; RB_INIT(&(session->sess_backend.ab_requests)); TAILQ_INSERT_TAIL(&(conn->conn_sessions), session, sess_conn_entry);
snmpd [11/16]: When a request results in EOMV we must return the requesting OID
According to RFC3416 section 4.2.2 and 4.2.3 case "(2)" when an endOfMibView is returned the OID must be identical to originally requested OID. Currently this can fail when the original request is in a !last registered region and all subsequent regions also return EOMV. Store the original OID and use that on EOMV. OK? martijn@ diff --git a/application.c b/application.c index e780025..dfa7220 100644 --- a/application.c +++ b/application.c @@ -99,6 +99,7 @@ struct appl_varbind_internal { enum appl_varbind_state avi_state; struct appl_varbind avi_varbind; struct appl_region *avi_region; + struct ber_oid avi_origid; int16_t avi_index; struct appl_request_upstream *avi_request_upstream; struct appl_request_downstream *avi_request_downstream; @@ -679,6 +680,8 @@ appl_processpdu(struct snmp_message *statereference, const char *ctxname, } ober_get_oid(varbind->be_sub, &(ureq->aru_vblist[i].avi_varbind.av_oid)); + ureq->aru_vblist[i].avi_origid = + ureq->aru_vblist[i].avi_varbind.av_oid; if (i + 1 < ureq->aru_varbindlen) { ureq->aru_vblist[i].avi_next = &(ureq->aru_vblist[i + 1]); @@ -1008,6 +1011,10 @@ appl_request_upstream_reply(struct appl_request_upstream *ureq) vb = &(ureq->aru_vblist[i]); vb->avi_varbind.av_next = &(ureq->aru_vblist[i + 1].avi_varbind); + value = vb->avi_varbind.av_value; + if (value->be_class == BER_CLASS_CONTEXT && + value->be_type == APPL_EXC_ENDOFMIBVIEW) + vb->avi_varbind.av_oid = vb->avi_origid; } ureq->aru_vblist[i - 1].avi_varbind.av_next = NULL;
snmpd [10/16]: Make retries on open session where connection is closed return early
Here's a special case unlikely to be found in the wild: When opening 2 sessions on an agentx connection (already unusual) and registering 2 overlapping regions on the different sessions, e.g. by differing in priority (even more unusual) and we close the underlying connection with an outstanding request to the dominant region we will call appl_agentx_free(), which sequentially closes all sessions. If the session with the outstanding request is closed before the second session the request is retried before said session is cleaned up and it will try to send it over a conn_ax which at that point has been set to NULL, resulting in a SIGSEGV. Simply return early and let this second request be cancelled by the cleanup of the second session. OK? martijn@ diff --git a/application_agentx.c b/application_agentx.c index 2231d4c..680725d 100644 --- a/application_agentx.c +++ b/application_agentx.c @@ -712,6 +712,9 @@ appl_agentx_get(struct appl_backend *backend, int32_t transactionid, struct ax_searchrange *srl; size_t i, j, nsr; + if (session->sess_conn->conn_ax == NULL) + return; + for (nsr = 0, vb = vblist; vb != NULL; vb = vb->av_next) nsr++; @@ -760,6 +763,9 @@ appl_agentx_getnext(struct appl_backend *backend, int32_t transactionid, struct ax_searchrange *srl; size_t i, j, nsr; + if (session->sess_conn->conn_ax == NULL) + return; + for (nsr = 0, vb = vblist; vb != NULL; vb = vb->av_next) nsr++;
snmpd [9/16]: Fix range handling with appl_unregister
Right now (un)registering a region with range_subid set to !0 will fail. Apparently nothing in the wild uses this, but let's fix it. This is the unregister part. OK? martijn@ diff --git a/application.c b/application.c index f8709b8..e780025 100644 --- a/application.c +++ b/application.c @@ -115,6 +115,8 @@ struct snmp_target_mib { enum appl_error appl_region(struct appl_context *, uint32_t, uint8_t, struct ber_oid *, int, int, struct appl_backend *); void appl_region_free(struct appl_context *, struct appl_region *); +enum appl_error appl_region_unregister_match(struct appl_context *, uint8_t, +struct ber_oid *, char *, struct appl_backend *, int); struct appl_region *appl_region_find(struct appl_context *, const struct ber_oid *); struct appl_region *appl_region_next(struct appl_context *, @@ -400,9 +402,10 @@ enum appl_error appl_unregister(const char *ctxname, uint8_t priority, struct ber_oid *oid, uint8_t range_subid, uint32_t upper_bound, struct appl_backend *backend) { - struct appl_region *region, search; struct appl_context *ctx; char oidbuf[1024], subidbuf[11]; + enum appl_error error; + uint32_t lower_bound; size_t i; oidbuf[0] = '\0'; @@ -444,34 +447,45 @@ appl_unregister(const char *ctxname, uint8_t priority, struct ber_oid *oid, return APPL_ERROR_PARSEERROR; } - if (range_subid > oid->bo_n) { + if (range_subid == 0) + return appl_region_unregister_match(ctx, priority, oid, oidbuf, + backend, 1); + + range_subid--; + if (range_subid >= oid->bo_n) { log_warnx("%s: Can't unregiser %s: range_subid too large", backend->ab_name, oidbuf); return APPL_ERROR_PARSEERROR; } - if (range_subid != 0 && oid->bo_id[range_subid] >= upper_bound) { - log_warnx("%s: Can't unregister %s: upper bound smaller or " - "equal to range_subid", backend->ab_name, oidbuf); + if (oid->bo_id[range_subid] > upper_bound) { + log_warnx("%s: Can't unregister %s: upper bound smaller than " + "range_subid", backend->ab_name, oidbuf); return APPL_ERROR_PARSEERROR; } + lower_bound = oid->bo_id[range_subid]; + do { + if ((error = appl_region_unregister_match(ctx, priority, oid, + oidbuf, backend, 0)) != APPL_ERROR_NOERROR) + return error; + } while (oid->bo_id[range_subid]++ != upper_bound); + + oid->bo_id[range_subid] = lower_bound; + do { + (void)appl_region_unregister_match(ctx, priority, oid, oidbuf, + backend, 1); + } while (oid->bo_id[range_subid]++ != upper_bound); + + return APPL_ERROR_NOERROR; +} + +enum appl_error +appl_region_unregister_match(struct appl_context *ctx, uint8_t priority, +struct ber_oid *oid, char *oidbuf, struct appl_backend *backend, int dofree) +{ + struct appl_region *region, search; + search.ar_oid = *oid; - while (range_subid != 0 && - search.ar_oid.bo_id[range_subid] != upper_bound) { - region = RB_FIND(appl_regions, &(ctx->ac_regions), &search); - while (region != NULL && region->ar_priority < priority) - region = region->ar_next; - if (region == NULL || region->ar_priority != priority) { - log_warnx("%s: Can't unregister %s: region not found", - backend->ab_name, oidbuf); - return APPL_ERROR_UNKNOWNREGISTRATION; - } - if (region->ar_backend != backend) { - log_warnx("%s: Can't unregister %s: region not owned " - "by backend", backend->ab_name, oidbuf); - return APPL_ERROR_UNKNOWNREGISTRATION; - } - } region = RB_FIND(appl_regions, &(ctx->ac_regions), &search); while (region != NULL && region->ar_priority < priority) region = region->ar_next; @@ -485,20 +499,8 @@ appl_unregister(const char *ctxname, uint8_t priority, struct ber_oid *oid, "by backend", backend->ab_name, oidbuf); return APPL_ERROR_UNKNOWNREGISTRATION; } - - search.ar_oid = *oid; - while (range_subid != 0 && - search.ar_oid.bo_id[range_subid] != upper_bound) { - region = RB_FIND(appl_regions, &(ctx->ac_regions), &search); - while (region != NULL && region->ar_priority != priority) - region = region->ar_next; + if (dofree) appl_region_free(ctx, region); - } - region = RB_FIND(appl_regions, &(ctx->ac_regions), &search); - while (region != NULL && region->ar_priority != priority) - region = region->ar_next; -
snmpd [8/16]: Fix range handling with appl_register
Right now (un)registering a region with range_subid set to !0 will fail. Apparently nothing in the wild uses this, but let's fix it. This is the register part. OK? martijn@ diff --git a/application.c b/application.c index a02260b..f8709b8 100644 --- a/application.c +++ b/application.c @@ -347,28 +347,29 @@ appl_register(const char *ctxname, uint32_t timeout, uint8_t priority, backend->ab_name, oidbuf); return APPL_ERROR_PARSEERROR; } - if (range_subid > oid->bo_n) { + + if (range_subid == 0) + return appl_region(ctx, timeout, priority, oid, instance, + subtree, backend); + + range_subid--; + if (range_subid >= oid->bo_n) { log_warnx("%s: Can't register %s: range_subid too large", backend->ab_name, oidbuf); return APPL_ERROR_PARSEERROR; } - if (range_subid != 0 && oid->bo_id[range_subid] >= upper_bound) { - log_warnx("%s: Can't register %s: upper bound smaller or equal " - "to range_subid", backend->ab_name, oidbuf); + if (oid->bo_id[range_subid] > upper_bound) { + log_warnx("%s: Can't register %s: upper bound smaller than " + "range_subid", backend->ab_name, oidbuf); return APPL_ERROR_PARSEERROR; } - if (range_subid != 0) - lower_bound = oid->bo_id[range_subid]; - - if (range_subid == 0) - return appl_region(ctx, timeout, priority, oid, instance, - subtree, backend); + lower_bound = oid->bo_id[range_subid]; do { if ((error = appl_region(ctx, timeout, priority, oid, instance, subtree, backend)) != APPL_ERROR_NOERROR) goto fail; - } while (oid->bo_id[range_subid] != upper_bound); + } while (oid->bo_id[range_subid]++ != upper_bound); if ((error = appl_region(ctx, timeout, priority, oid, instance, subtree, backend)) != APPL_ERROR_NOERROR) goto fail;
snmpd [7/16]: Treat agentx-close-pdu with reasonByManager as a parseerror
RFC2741 section 6.2.2 says that reasonByManager can only be used by the agentx master. Treat this reason as a parseerror. OK? martijn@ diff --git a/application_agentx.c b/application_agentx.c index d1efae2..2231d4c 100644 --- a/application_agentx.c +++ b/application_agentx.c @@ -576,16 +576,29 @@ appl_agentx_close(struct appl_agentx_session *session, struct ax_pdu *pdu) { struct appl_agentx_connection *conn = session->sess_conn; char name[100]; + enum appl_error error = APPL_ERROR_NOERROR; strlcpy(name, session->sess_backend.ab_name, sizeof(name)); - appl_agentx_session_free(session); - log_info("%s: Closed by subagent (%s)", name, - ax_closereason2string(pdu->ap_payload.ap_close.ap_reason)); + if (pdu->ap_payload.ap_close.ap_reason == AX_CLOSE_BYMANAGER) { + log_warnx("%s: Invalid close reason", name); + error = APPL_ERROR_PARSEERROR; + } else { + appl_agentx_session_free(session); + log_info("%s: Closed by subagent (%s)", name, + ax_closereason2string(pdu->ap_payload.ap_close.ap_reason)); + } ax_response(conn->conn_ax, pdu->ap_header.aph_sessionid, pdu->ap_header.aph_transactionid, pdu->ap_header.aph_packetid, - smi_getticks(), APPL_ERROR_NOERROR, 0, NULL, 0); + smi_getticks(), error, 0, NULL, 0); appl_agentx_send(-1, EV_WRITE, conn); + if (error == APPL_ERROR_NOERROR) + return; + + appl_agentx_forceclose(&(session->sess_backend), + APPL_CLOSE_REASONPARSEERROR); + if (TAILQ_EMPTY(&(conn->conn_sessions))) + appl_agentx_free(conn, APPL_CLOSE_REASONOTHER); } void
snmpd [6/16]: support close reason for appl_agentx_free
appl_agentx_free() closes any potential open sessions before closing the connection and cleaning up. This function is called from multiple contexts and the current APPL_CLOSE_REASONSHUTDOWN is not always applicable. Add a second reason parameter that can be passed onto appl_agentx_forceclose(). OK? martijn@ diff --git a/application_agentx.c b/application_agentx.c index 6a4ed49..d1efae2 100644 --- a/application_agentx.c +++ b/application_agentx.c @@ -77,7 +77,7 @@ struct appl_agentx_session { void appl_agentx_listen(struct agentx_master *); void appl_agentx_accept(int, short, void *); -void appl_agentx_free(struct appl_agentx_connection *); +void appl_agentx_free(struct appl_agentx_connection *, enum appl_close_reason); void appl_agentx_recv(int, short, void *); void appl_agentx_open(struct appl_agentx_connection *, struct ax_pdu *); void appl_agentx_close(struct appl_agentx_session *, struct ax_pdu *); @@ -178,7 +178,7 @@ appl_agentx_shutdown(void) struct appl_agentx_connection *conn, *tconn; RB_FOREACH_SAFE(conn, appl_agentx_conns, &appl_agentx_conns, tconn) - appl_agentx_free(conn); + appl_agentx_free(conn, APPL_CLOSE_REASONSHUTDOWN); } void @@ -249,7 +249,8 @@ appl_agentx_backend(int fd) } void -appl_agentx_free(struct appl_agentx_connection *conn) +appl_agentx_free(struct appl_agentx_connection *conn, +enum appl_close_reason reason) { struct appl_agentx_session *session; @@ -261,7 +262,7 @@ appl_agentx_free(struct appl_agentx_connection *conn) appl_agentx_session_free(session); else appl_agentx_forceclose(&(session->sess_backend), - APPL_CLOSE_REASONSHUTDOWN); + reason); } RB_REMOVE(appl_agentx_conns, &appl_agentx_conns, conn); @@ -294,7 +295,8 @@ appl_agentx_recv(int fd, short event, void *cookie) ax_free(conn->conn_ax); conn->conn_ax = NULL; } - appl_agentx_free(conn); + appl_agentx_free(conn, errno == EPROTO ? + APPL_CLOSE_REASONPROTOCOLERROR : APPL_CLOSE_REASONOTHER); return; } @@ -458,7 +460,7 @@ appl_agentx_recv(int fd, short event, void *cookie) appl_agentx_forceclose(&(session->sess_backend), APPL_CLOSE_REASONPARSEERROR); if (TAILQ_EMPTY(&(conn->conn_sessions))) - appl_agentx_free(conn); + appl_agentx_free(conn, APPL_CLOSE_REASONOTHER); } void @@ -847,7 +849,7 @@ appl_agentx_send(int fd, short event, void *cookie) log_warn("AgentX(%"PRIu32")", conn->conn_id); ax_free(conn->conn_ax); conn->conn_ax = NULL; - appl_agentx_free(conn); + appl_agentx_free(conn, APPL_CLOSE_REASONOTHER); return; case 0: return;
snmpd [5/16]: Check context existence in appl_agentx_recv
application.c checks the context where applicable, but not every agentx-pdu goes through there (e.g. agentx-ping-pdu). Make sure we always check the context in appl_agentx_recv() OK? martijn@ diff --git a/application.c b/application.c index dd92864..a02260b 100644 --- a/application.c +++ b/application.c @@ -175,7 +175,7 @@ appl_shutdown(void) } } -static struct appl_context * +struct appl_context * appl_context(const char *name, int create) { struct appl_context *ctx; diff --git a/application.h b/application.h index 8b2c567..949bbde 100644 --- a/application.h +++ b/application.h @@ -121,6 +121,7 @@ struct appl_backend { void appl(void); void appl_init(void); void appl_shutdown(void); +struct appl_context *appl_context(const char *, int); enum appl_error appl_register(const char *, uint32_t, uint8_t, struct ber_oid *, int, int, uint8_t, uint32_t, struct appl_backend *); enum appl_error appl_unregister(const char *, uint8_t, struct ber_oid *, diff --git a/application_agentx.c b/application_agentx.c index c11b666..6a4ed49 100644 --- a/application_agentx.c +++ b/application_agentx.c @@ -370,6 +370,12 @@ appl_agentx_recv(int fd, short event, void *cookie) error = APPL_ERROR_PARSEERROR; goto fail; } + if (appl_context(pdu->ap_context.aos_string, 0) == 0) { + log_warnx("%s: %s: Unsupported context", + name, ax_pdutype2string(pdu->ap_header.aph_flags)); + error = APPL_ERROR_UNSUPPORTEDCONTEXT; + goto fail; + } } switch (pdu->ap_header.aph_type) { case AX_PDU_TYPE_OPEN:
snmpd [4/16]: check agentx-pdu-header flags for validity
RFC2741 section 6.1 specifies which PDUs can contain which header flags. Check that that incoming agentx PDUs have valid flags in appl_agentx_recv(). While here I cleaned up a few log messages some minor restructuring to prevent the function growing too large. OK? martijn@ diff --git a/application_agentx.c b/application_agentx.c index 9cc98eb..c11b666 100644 --- a/application_agentx.c +++ b/application_agentx.c @@ -276,13 +276,16 @@ void appl_agentx_recv(int fd, short event, void *cookie) { struct appl_agentx_connection *conn = cookie; - struct appl_agentx_session *session; + struct appl_agentx_session *session = NULL; struct ax_pdu *pdu; + enum appl_error error; + char name[100]; + snprintf(name, sizeof(name), "AgentX(%"PRIu32")", conn->conn_id); if ((pdu = ax_recv(conn->conn_ax)) == NULL) { if (errno == EAGAIN) return; - log_warn("AgentX(%"PRIu32")", conn->conn_id); + log_warn("%s", name); /* * Either the connection is dead, or we had garbage on the line. * Both make sure we can't continue on this stream. @@ -306,16 +309,12 @@ appl_agentx_recv(int fd, short event, void *cookie) break; } if (session == NULL) { - log_warnx("AgentX(%"PRIu32"): Session %"PRIu32" not " - "found for request", conn->conn_id, - pdu->ap_header.aph_sessionid); - ax_response(conn->conn_ax, pdu->ap_header.aph_sessionid, - pdu->ap_header.aph_transactionid, - pdu->ap_header.aph_packetid, smi_getticks(), - APPL_ERROR_NOTOPEN, 0, NULL, 0); - appl_agentx_send(-1, EV_WRITE, conn); + log_warnx("%s: Session %"PRIu32" not found for request", + name, pdu->ap_header.aph_sessionid); + error = APPL_ERROR_NOTOPEN; goto fail; } + strlcpy(name, session->sess_backend.ab_name, sizeof(name)); /* * RFC2741 section 7.1.1 bullet 4 is unclear on what byte order * the response should be. My best guess is that it makes more @@ -327,6 +326,51 @@ appl_agentx_recv(int fd, short event, void *cookie) */ } + if (pdu->ap_header.aph_flags & AX_PDU_FLAG_INSTANCE_REGISTRATION) { + if (pdu->ap_header.aph_type != AX_PDU_TYPE_REGISTER) { + log_warnx("%s: %s: Invalid INSTANCE_REGISTRATION flag", + name, ax_pdutype2string(pdu->ap_header.aph_flags)); + error = APPL_ERROR_PARSEERROR; + goto fail; + } + } + if (pdu->ap_header.aph_flags & AX_PDU_FLAG_NEW_INDEX) { + if (pdu->ap_header.aph_type != AX_PDU_TYPE_INDEXALLOCATE && + pdu->ap_header.aph_type != AX_PDU_TYPE_INDEXDEALLOCATE) { + log_warnx("%s: %s: Invalid NEW_INDEX flag", name, + ax_pdutype2string(pdu->ap_header.aph_flags)); + error = APPL_ERROR_PARSEERROR; + goto fail; + } + } + if (pdu->ap_header.aph_flags & AX_PDU_FLAG_ANY_INDEX) { + if (pdu->ap_header.aph_type != AX_PDU_TYPE_INDEXALLOCATE && + pdu->ap_header.aph_type != AX_PDU_TYPE_INDEXDEALLOCATE) { + log_warnx("%s: %s: Invalid ANY_INDEX flag", name, + ax_pdutype2string(pdu->ap_header.aph_flags)); + error = APPL_ERROR_PARSEERROR; + goto fail; + } + } + if (pdu->ap_header.aph_flags & AX_PDU_FLAG_NON_DEFAULT_CONTEXT) { + if (pdu->ap_header.aph_type != AX_PDU_TYPE_REGISTER && + pdu->ap_header.aph_type != AX_PDU_TYPE_UNREGISTER && + pdu->ap_header.aph_type != AX_PDU_TYPE_ADDAGENTCAPS && + pdu->ap_header.aph_type != AX_PDU_TYPE_REMOVEAGENTCAPS && + pdu->ap_header.aph_type != AX_PDU_TYPE_GET && + pdu->ap_header.aph_type != AX_PDU_TYPE_GETNEXT && + pdu->ap_header.aph_type != AX_PDU_TYPE_GETBULK && + pdu->ap_header.aph_type != AX_PDU_TYPE_INDEXALLOCATE && + pdu->ap_header.aph_type != AX_PDU_TYPE_INDEXDEALLOCATE && + pdu->ap_header.aph_type != AX_PDU_TYPE_NOTIFY && + pdu->ap_header.aph_type != AX_PDU_TYPE_TESTSET && + pdu->ap_header.aph_type != AX_PDU_TYPE_PING) { + log_warnx("%s: %s: Invalid NON_DEFAULT_CONTEXT flag", + name, ax_pdutype2string(pdu->ap_header.ap
snmpd [3/16]: Distinguish between parseerror and openfailed for agentx-open-pdu
RFC2741 section 7.1.1 tells us that if a pdu can't be parsed we must return a parseerror. Section 7.1 gives an example of "An unrecognized value is encountered". The spec is vague is a bit vague on what constitutes a parseerror, vs a protocol error, so I don't want to muddle too much with that part, but let's at least return an appropriate error when a client sends invalid data in the open request. The spec clearly states that any error, which is not a parseerror must be a openfailed error. So no processingerrors. OK? martijn@ diff --git a/application_agentx.c b/application_agentx.c index 0d73e08..9cc98eb 100644 --- a/application_agentx.c +++ b/application_agentx.c @@ -411,9 +411,11 @@ appl_agentx_open(struct appl_agentx_connection *conn, struct ax_pdu *pdu) struct appl_agentx_session *session; struct ber_oid oid; char oidbuf[1024]; + enum appl_error error = APPL_ERROR_NOERROR; if ((session = malloc(sizeof(*session))) == NULL) { log_warn(NULL); + error = APPL_ERROR_OPENFAILED; goto fail; } session->sess_descr.aos_string = NULL; @@ -432,12 +434,14 @@ appl_agentx_open(struct appl_agentx_connection *conn, struct ax_pdu *pdu) } else if (pdu->ap_payload.ap_open.ap_oid.aoi_idlen == 1) { log_warnx("AgentX(%"PRIu32"): Invalid oid: Open Failed", conn->conn_id); + error = APPL_ERROR_PARSEERROR; goto fail; } /* RFC 2742 agentxSessionDescr */ if (pdu->ap_payload.ap_open.ap_descr.aos_slen > 255) { log_warnx("AgentX(%"PRIu32"): Invalid descr (too long): Open " "Failed", conn->conn_id); + error = APPL_ERROR_PARSEERROR; goto fail; } /* @@ -451,6 +455,7 @@ appl_agentx_open(struct appl_agentx_connection *conn, struct ax_pdu *pdu) pdu->ap_payload.ap_open.ap_descr.aos_string, 0) == (size_t)-1) { log_warnx("AgentX(%"PRIu32"): Invalid descr (not UTF-8): " "Open Failed", conn->conn_id); + error = APPL_ERROR_PARSEERROR; goto fail; } @@ -463,6 +468,7 @@ appl_agentx_open(struct appl_agentx_connection *conn, struct ax_pdu *pdu) if (session->sess_descr.aos_string == NULL) { log_warn("AgentX(%"PRIu32"): strdup: Open Failed", conn->conn_id); + error = APPL_ERROR_OPENFAILED; goto fail; } } @@ -478,6 +484,7 @@ appl_agentx_open(struct appl_agentx_connection *conn, struct ax_pdu *pdu) conn->conn_id, session->sess_id) == -1) { log_warn("AgentX(%"PRIu32"): asprintf: Open Failed", conn->conn_id); + error = APPL_ERROR_OPENFAILED; goto fail; } session->sess_backend.ab_cookie = session; @@ -499,7 +506,7 @@ appl_agentx_open(struct appl_agentx_connection *conn, struct ax_pdu *pdu) return; fail: ax_response(conn->conn_ax, 0, pdu->ap_header.aph_transactionid, - pdu->ap_header.aph_packetid, 0, APPL_ERROR_OPENFAILED, 0, NULL, 0); + pdu->ap_header.aph_packetid, 0, error, 0, NULL, 0); appl_agentx_send(-1, EV_WRITE, conn); if (session != NULL) free(session->sess_descr.aos_string);
snmpd [2.2/16]: Don't set NON_DEFAULT_CONTEXT for agentx-response-pdu
> According to RFC2741 section 6.1.1 an agentx-response-pdu shouldn't have > the NON_DEFAULT_CONTEXT set. Remove the argument from ax_response(). Here's the libagentx counterpart. OK? martijn@ Index: agentx.c === RCS file: /cvs/src/lib/libagentx/agentx.c,v retrieving revision 1.22 diff -u -p -r1.22 agentx.c --- agentx.c27 Dec 2022 17:10:05 - 1.22 +++ agentx.c9 Oct 2023 20:24:40 - @@ -2733,8 +2733,7 @@ agentx_get_finalize(struct agentx_get *a free(logmsg); if (ax_response(ax->ax_ax, axs->axs_id, axg->axg_transactionid, - axg->axg_packetid, AGENTX_CONTEXT_CTX(axc), 0, error, index, - vbl, nvarbind) == -1) { + axg->axg_packetid, 0, error, index, vbl, nvarbind) == -1) { agentx_log_axg_warn(axg, "Couldn't parse request"); agentx_reset(ax); } else @@ -4041,7 +4040,6 @@ agentx_read(struct agentx *ax) if (ax_response(ax->ax_ax, axs->axs_id, pdu->ap_header.aph_transactionid, pdu->ap_header.aph_packetid, - axc == NULL ? NULL : AGENTX_CONTEXT_CTX(axc), 0, error, 1, NULL, 0) == -1) agentx_log_axc_warn(axc, "transaction: %u packetid: %u: failed to send " Index: ax.c === RCS file: /cvs/src/lib/libagentx/ax.c,v retrieving revision 1.8 diff -u -p -r1.8 ax.c --- ax.c24 Oct 2021 17:43:38 - 1.8 +++ ax.c9 Oct 2023 20:24:40 - @@ -553,11 +553,11 @@ ax_unregister(struct ax *ax, uint32_t se int ax_response(struct ax *ax, uint32_t sessionid, uint32_t transactionid, -uint32_t packetid, struct ax_ostring *context, uint32_t sysuptime, -uint16_t error, uint16_t index, struct ax_varbind *vblist, size_t nvb) +uint32_t packetid, uint32_t sysuptime, uint16_t error, uint16_t index, +struct ax_varbind *vblist, size_t nvb) { if (ax_pdu_header(ax, AX_PDU_TYPE_RESPONSE, 0, sessionid, - transactionid, packetid, context) == -1) + transactionid, packetid, NULL) == -1) return -1; if (ax_pdu_add_uint32(ax, sysuptime) == -1 || Index: ax.h === RCS file: /cvs/src/lib/libagentx/ax.h,v retrieving revision 1.4 diff -u -p -r1.4 ax.h --- ax.h2 Jan 2021 01:06:31 - 1.4 +++ ax.h9 Oct 2023 20:24:40 - @@ -220,8 +220,7 @@ uint32_t ax_register(struct ax *, uint8_ uint32_t ax_unregister(struct ax *, uint32_t, struct ax_ostring *, uint8_t, uint8_t, struct ax_oid *, uint32_t); int ax_response(struct ax *, uint32_t, uint32_t, uint32_t, -struct ax_ostring *, uint32_t, uint16_t, uint16_t, -struct ax_varbind *, size_t); +uint32_t, uint16_t, uint16_t, struct ax_varbind *, size_t); void ax_pdu_free(struct ax_pdu *); void ax_varbind_free(struct ax_varbind *); const char *ax_error2string(enum ax_pdu_error);
Re: snmpd [1.1/16]: Don't overflow oid in agentx parser
> Currently ax.c doesn't check the maximum length of an OID ax_pdutooid. > This can lead to a buffer overflow. Even though it must be fixed, I > don't think there's a big risk here, since an attacker would need to have > access to the agentx socket, which by default is disabled and defaults > to root:_agentx when enabled. Here's the libagentx counterpart. OK? martijn@ Index: ax.c === RCS file: /cvs/src/lib/libagentx/ax.c,v retrieving revision 1.8 diff -u -p -r1.8 ax.c --- ax.c24 Oct 2021 17:43:38 - 1.8 +++ ax.c9 Oct 2023 20:14:13 - @@ -1262,6 +1262,8 @@ ax_pdutooid(struct ax_pdu_header *header } buf++; oid->aoi_include = *buf; + if (oid->aoi_idlen > AX_OID_MAX_LEN) + goto fail; for (buf += 2; i < oid->aoi_idlen; i++, buf += 4) oid->aoi_id[i] = ax_pdutoh32(header, buf);
snmpd [2.1/16]: Don't set NON_DEFAULT_CONTEXT for agentx-response-pdu
According to RFC2741 section 6.1.1 an agentx-response-pdu shouldn't have the NON_DEFAULT_CONTEXT set. Remove the argument from ax_response(). OK? martijn@ diff --git a/application_agentx.c b/application_agentx.c index c7a2a26..0d73e08 100644 --- a/application_agentx.c +++ b/application_agentx.c @@ -311,8 +311,7 @@ appl_agentx_recv(int fd, short event, void *cookie) pdu->ap_header.aph_sessionid); ax_response(conn->conn_ax, pdu->ap_header.aph_sessionid, pdu->ap_header.aph_transactionid, - pdu->ap_header.aph_packetid, - &(pdu->ap_context), smi_getticks(), + pdu->ap_header.aph_packetid, smi_getticks(), APPL_ERROR_NOTOPEN, 0, NULL, 0); appl_agentx_send(-1, EV_WRITE, conn); goto fail; @@ -370,8 +369,8 @@ appl_agentx_recv(int fd, short event, void *cookie) case AX_PDU_TYPE_PING: ax_response(conn->conn_ax, pdu->ap_header.aph_sessionid, pdu->ap_header.aph_transactionid, - pdu->ap_header.aph_packetid, &(pdu->ap_context), - smi_getticks(), APPL_ERROR_NOERROR, 0, NULL, 0); + pdu->ap_header.aph_packetid, smi_getticks(), + APPL_ERROR_NOERROR, 0, NULL, 0); appl_agentx_send(-1, EV_WRITE, conn); break; case AX_PDU_TYPE_INDEXALLOCATE: @@ -380,8 +379,8 @@ appl_agentx_recv(int fd, short event, void *cookie) ax_pdutype2string(pdu->ap_header.aph_type)); ax_response(conn->conn_ax, pdu->ap_header.aph_sessionid, pdu->ap_header.aph_transactionid, - pdu->ap_header.aph_packetid, &(pdu->ap_context), - smi_getticks(), APPL_ERROR_PROCESSINGERROR, 1, + pdu->ap_header.aph_packetid, smi_getticks(), + APPL_ERROR_PROCESSINGERROR, 1, pdu->ap_payload.ap_vbl.ap_varbind, pdu->ap_payload.ap_vbl.ap_nvarbind); appl_agentx_send(-1, EV_WRITE, conn); @@ -392,8 +391,8 @@ appl_agentx_recv(int fd, short event, void *cookie) ax_pdutype2string(pdu->ap_header.aph_type)); ax_response(conn->conn_ax, pdu->ap_header.aph_sessionid, pdu->ap_header.aph_transactionid, - pdu->ap_header.aph_packetid, &(pdu->ap_context), - smi_getticks(), APPL_ERROR_PROCESSINGERROR, 1, + pdu->ap_header.aph_packetid, smi_getticks(), + APPL_ERROR_PROCESSINGERROR, 1, NULL, 0); appl_agentx_send(-1, EV_WRITE, conn); break; @@ -492,16 +491,15 @@ appl_agentx_open(struct appl_agentx_connection *conn, struct ax_pdu *pdu) log_info("%s: %s %s: Open", session->sess_backend.ab_name, oidbuf, session->sess_descr.aos_string); - ax_response(conn->conn_ax, session->sess_id, pdu->ap_header.aph_transactionid, - pdu->ap_header.aph_packetid, NULL, smi_getticks(), APPL_ERROR_NOERROR, 0, - NULL, 0); + ax_response(conn->conn_ax, session->sess_id, + pdu->ap_header.aph_transactionid, pdu->ap_header.aph_packetid, + smi_getticks(), APPL_ERROR_NOERROR, 0, NULL, 0); appl_agentx_send(-1, EV_WRITE, conn); return; fail: ax_response(conn->conn_ax, 0, pdu->ap_header.aph_transactionid, - pdu->ap_header.aph_packetid, NULL, 0, APPL_ERROR_OPENFAILED, 0, - NULL, 0); + pdu->ap_header.aph_packetid, 0, APPL_ERROR_OPENFAILED, 0, NULL, 0); appl_agentx_send(-1, EV_WRITE, conn); if (session != NULL) free(session->sess_descr.aos_string); @@ -521,7 +519,7 @@ appl_agentx_close(struct appl_agentx_session *session, struct ax_pdu *pdu) ax_response(conn->conn_ax, pdu->ap_header.aph_sessionid, pdu->ap_header.aph_transactionid, pdu->ap_header.aph_packetid, - &(pdu->ap_context), smi_getticks(), APPL_ERROR_NOERROR, 0, NULL, 0); + smi_getticks(), APPL_ERROR_NOERROR, 0, NULL, 0); appl_agentx_send(-1, EV_WRITE, conn); } @@ -593,7 +591,7 @@ appl_agentx_register(struct appl_agentx_session *session, struct ax_pdu *pdu) fail: ax_response(session->sess_conn->conn_ax, session->sess_id, pdu->ap_header.aph_transactionid, pdu->ap_header.aph_packetid, - &(pdu->ap_context), smi_getticks(), error, 0, NULL, 0); + smi_getticks(), error, 0, NULL, 0); appl_agentx_send(-1, EV_WRITE, session->sess_conn); } @@ -620,7 +618,7 @@ appl_agentx_unregister(struct appl_agentx_session *session, struct ax_pdu *pdu) fail: ax_response(session->sess_conn->conn_ax, session->sess_id, pdu->ap_header.aph_transactionid, pdu->ap_header.aph_packetid
snmpd [1.1/16]: Don't overflow oid in agentx parser
Currently ax.c doesn't check the maximum length of an OID ax_pdutooid. This can lead to a buffer overflow. Even though it must be fixed, I don't think there's a big risk here, since an attacker would need to have access to the agentx socket, which by default is disabled and defaults to root:_agentx when enabled. OK? martijn@ diff --git a/ax.c b/ax.c index 63add68..27580a6 100644 --- a/ax.c +++ b/ax.c @@ -1442,6 +1442,8 @@ ax_pdutooid(struct ax_pdu_header *header, struct ax_oid *oid, } buf++; oid->aoi_include = *buf; + if (oid->aoi_idlen > AX_OID_MAX_LEN) + goto fail; for (buf += 2; i < oid->aoi_idlen; i++, buf += 4) oid->aoi_id[i] = ax_pdutoh32(header, buf);
Re: ober_scanf_elements() and empty sequences
On Tue, 2023-08-22 at 10:16 +, Gerhard Roth wrote: > On Tue, 2023-08-22 at 11:16 +0200, Martijn van Duren wrote: > > On Mon, 2023-08-21 at 07:35 +, Gerhard Roth wrote: > > > Hi Martijn, > > > > > > last November you fixed ber.c so that sequences won't generate > > > an uninitialized subelement. > > > > > > This revealed another bug in ober_scanf_elements(): it couldn't > > > process sequences with an empty list of subelements. The following > > > code failed in ober_scanf_elements(): > > > > > > struct ber_element *root; > > > struct ber_element *sub; > > > > > > if ((root = ober_add_sequence(NULL)) == NULL) > > > err(1, "ober_add_sequence() failed"); > > > > > > errno = 0; > > > if (ober_scanf_elements(root, "{e", &sub)) > > > err(1, "ober_scanf_elements() failed"); > > > > > > printf("sub = %p\n", sub); > > > > > > > > > The patch below fixes that. > > > > > > Gerhard > > > > > > > > > Index: lib/libutil/ber.c > > > === > > > RCS file: /cvs/src/lib/libutil/ber.c,v > > > retrieving revision 1.24 > > > diff -u -p -u -p -r1.24 ber.c > > > --- lib/libutil/ber.c 3 Nov 2022 17:58:10 - 1.24 > > > +++ lib/libutil/ber.c 21 Aug 2023 07:24:21 - > > > @@ -700,7 +700,8 @@ ober_scanf_elements(struct ber_element * > > > > > > va_start(ap, fmt); > > > while (*fmt) { > > > - if (ber == NULL && *fmt != '$' && *fmt != '}' && *fmt != > > > ')') > > > + if (ber == NULL && *fmt != '$' && *fmt != '}' && *fmt != > > > ')' && > > > + *fmt != 'e') > > > goto fail; > > > > I'm not sure about this part. An ober_scanf_elements of "{}e" on your > > example above also fails. The 'e' element might not increment the ber > > pointer, but I do think it should fail if an expected element is > > missing. > > You're right. And digging into it, only makes it look worse. > > I think, the 'e' format should behave differently. > > 1) in case of '{e' it is the first element of a sequence. > Empty sequences are allowed and we should not fail but > set the argument to NULL instead. > > 2) in all other cases, 'e' is the next element in the list. > And here we should fail if there are less elements in the > list than expected. > > Below is an updated diff that fixes the problem by remembering > whether the last traversal was via 'be_sub' (fsub == 1) or 'be_next' > and then behaves differently. Not very elegant although. I'm not sure yet. Do you have a particular usecase for this? Usually you would just do something like ober_scanf_elements(elm, "e{", seq); if (seq->be_sub != NULL) ... > > > > > > switch (*fmt++) { > > > case '$': > > > @@ -797,7 +798,7 @@ ober_scanf_elements(struct ber_element * > > > if (ber->be_encoding != BER_TYPE_SEQUENCE && > > > ber->be_encoding != BER_TYPE_SET) > > > goto fail; > > > - if (ber->be_sub == NULL || level >= _MAX_SEQ-1) > > > + if (level >= _MAX_SEQ-1) > > > > This part is OK martijn@ > > > > > goto fail; > > > parent[++level] = ber; > > > ber = ber->be_sub; > > > > > > > Index: lib/libutil/ber.c > === > RCS file: /cvs/src/lib/libutil/ber.c,v > retrieving revision 1.24 > diff -u -p -u -p -r1.24 ber.c > --- lib/libutil/ber.c 3 Nov 2022 17:58:10 - 1.24 > +++ lib/libutil/ber.c 22 Aug 2023 10:10:43 - > @@ -695,12 +695,14 @@ ober_scanf_elements(struct ber_element * > off_t *pos; > struct ber_oid *o; > struct ber_element *parent[_MAX_SEQ], **e; > + int
Re: ober_scanf_elements() and empty sequences
On Mon, 2023-08-21 at 07:35 +, Gerhard Roth wrote: > Hi Martijn, > > last November you fixed ber.c so that sequences won't generate > an uninitialized subelement. > > This revealed another bug in ober_scanf_elements(): it couldn't > process sequences with an empty list of subelements. The following > code failed in ober_scanf_elements(): > > struct ber_element *root; > struct ber_element *sub; > > if ((root = ober_add_sequence(NULL)) == NULL) > err(1, "ober_add_sequence() failed"); > > errno = 0; > if (ober_scanf_elements(root, "{e", &sub)) > err(1, "ober_scanf_elements() failed"); > > printf("sub = %p\n", sub); > > > The patch below fixes that. > > Gerhard > > > Index: lib/libutil/ber.c > === > RCS file: /cvs/src/lib/libutil/ber.c,v > retrieving revision 1.24 > diff -u -p -u -p -r1.24 ber.c > --- lib/libutil/ber.c 3 Nov 2022 17:58:10 - 1.24 > +++ lib/libutil/ber.c 21 Aug 2023 07:24:21 - > @@ -700,7 +700,8 @@ ober_scanf_elements(struct ber_element * > > va_start(ap, fmt); > while (*fmt) { > - if (ber == NULL && *fmt != '$' && *fmt != '}' && *fmt != ')') > + if (ber == NULL && *fmt != '$' && *fmt != '}' && *fmt != ')' && > + *fmt != 'e') > goto fail; I'm not sure about this part. An ober_scanf_elements of "{}e" on your example above also fails. The 'e' element might not increment the ber pointer, but I do think it should fail if an expected element is missing. > switch (*fmt++) { > case '$': > @@ -797,7 +798,7 @@ ober_scanf_elements(struct ber_element * > if (ber->be_encoding != BER_TYPE_SEQUENCE && > ber->be_encoding != BER_TYPE_SET) > goto fail; > - if (ber->be_sub == NULL || level >= _MAX_SEQ-1) > + if (level >= _MAX_SEQ-1) This part is OK martijn@ > goto fail; > parent[++level] = ber; > ber = ber->be_sub; >
ber.c: Don't best effort on unknown encodings
This one has been irking me for some time. At the moment when decoding a ber message there is no way for br_application to indicate that the tag/type is unrecognised and if br_application is not set we default to BER_TYPE_NULL. In basically all cases any wrong encoding should be caught by ober_scanf_elements and friends, but I'd rather fail sooner than later. By scanning the tree I found BER_TYPE_DEFAULT only used by ber.c, so I decided to rename it to BER_TYPE_UNKNOWN so that it can be used as a failure return value from br_application. The value of -1 can safely be used here, since get_id() allows max sizeof(unsigned int) iterations and each iteration adds 7 bits to the value, so we never reach negative numbers through natural means. In a similar fashion: don't simply ignore elements in ober_write_elements() if the encoding is not recognised. ober_set_application() is currently not documented, so on this front there is no update, I think this should be done as a separate endeavour. I've put this diff through snmp, snmpd, ldapd, libutil regress without problems, but I can't fully rule out any fallout (valid, or invalid). thoughts? OK? martijn@ Index: ber.c === RCS file: /cvs/src/lib/libutil/ber.c,v retrieving revision 1.24 diff -u -p -r1.24 ber.c --- ber.c 3 Nov 2022 17:58:10 - 1.24 +++ ber.c 23 Dec 2022 11:42:38 - @@ -64,7 +64,7 @@ ober_get_element(unsigned int encoding) return NULL; elm->be_encoding = encoding; - ober_set_header(elm, BER_CLASS_UNIVERSAL, BER_TYPE_DEFAULT); + ober_set_header(elm, BER_CLASS_UNIVERSAL, BER_TYPE_UNKNOWN); return elm; } @@ -73,7 +73,7 @@ void ober_set_header(struct ber_element *elm, int class, unsigned int type) { elm->be_class = class & BER_CLASS_MASK; - if (type == BER_TYPE_DEFAULT) + if (type == BER_TYPE_UNKNOWN) type = elm->be_encoding; elm->be_type = type; } @@ -899,7 +899,7 @@ ober_read_elements(struct ber *ber, stru struct ber_element *root = elm; if (root == NULL) { - if ((root = ober_get_element(0)) == NULL) + if ((root = ober_get_element(BER_TYPE_UNKNOWN)) == NULL) return NULL; } @@ -1078,6 +1078,9 @@ ober_dump_element(struct ber *ber, struc if (root->be_sub && ober_dump_element(ber, root->be_sub) == -1) return -1; break; + default: + errno = EINVAL; + return -1; } if (root->be_next == NULL) @@ -1291,7 +1294,7 @@ ober_read_element(struct ber *ber, struc elm->be_offs = ber->br_offs;/* element position within stream */ elm->be_class = class; - if (elm->be_encoding == 0) { + if (elm->be_encoding == BER_TYPE_UNKNOWN) { /* try to figure out the encoding via class, type and cstruct */ if (cstruct) elm->be_encoding = BER_TYPE_SEQUENCE; @@ -1304,9 +1307,12 @@ ober_read_element(struct ber *ber, struc * type is defined as 4 byte OCTET STRING. */ elm->be_encoding = (*ber->br_application)(elm); - } else - /* last resort option */ - elm->be_encoding = BER_TYPE_NULL; + } + + if (elm->be_encoding == BER_TYPE_UNKNOWN) { + errno = EINVAL; + return -1; + } } switch (elm->be_encoding) { @@ -1376,7 +1382,8 @@ ober_read_element(struct ber *ber, struc case BER_TYPE_SEQUENCE: case BER_TYPE_SET: if (len > 0 && elm->be_sub == NULL) { - if ((elm->be_sub = ober_get_element(0)) == NULL) + elm->be_sub = ober_get_element(BER_TYPE_UNKNOWN); + if (elm->be_sub == NULL) return -1; } next = elm->be_sub; @@ -1403,7 +1410,8 @@ ober_read_element(struct ber *ber, struc elements++; len -= r; if (len > 0 && next->be_next == NULL) { - next->be_next = ober_get_element(0); + next->be_next = + ober_get_element(BER_TYPE_UNKNOWN); if (next->be_next == NULL) return -1; } Index: ber.h === RCS file: /cvs/src/lib/libutil/ber.h,v retrieving revision 1.5 diff -u -p -r1.5 ber.h --- ber.h 31 Oct 2021 16:42:08 - 1.5 +++ ber.h 23 Dec 2022 11:42:38 - @@ -58,7 +58,7 @@ struct ber { }; /* well-known ber_element types */ -#d
Re: 7.2: snmp mibtree command broken
On Tue, 2022-12-20 at 10:28 +, Gerhard Roth wrote: > Hi Martijn, > > On Tue, 2022-12-20 at 11:10 +0100, Martijn van Duren wrote: > > On Tue, 2022-12-20 at 10:21 +0100, Matthias Pitzl wrote: > > > Hi, > > > > > > Since the release of OpenBSD 7.2, snmp mibtree is broken: > > > > root@host:~# snmp mibtree > > > > snmp: No securityName specified > > > > > > Greetings, > > > Matthias > > > > Apparently no one has used this one in a long time (including me). > > This got broken in snmpc.c r1.37 on 2021/08/11 when changing snmp(1)'s > > default to v3. > > > > This may not the prettiest solution, but this is the shortest I can come > > up with. > > no sub-command that hasn't 'snmp_app->usecommonopt' set can have any of > the SNMPv2 or SNMPv3 credentials. Sure, currently 'mibtree' is the only > one. But for future updates, wouldn't it be better to check > > if (!snmp_app->usecommonopt) > > instead of > > if (strcmp(snmp_app->name, "mibtree") == 0) > > Greetings, > > Gerhard > > You're right, that's better. Index: snmpc.c === RCS file: /cvs/src/usr.bin/snmp/snmpc.c,v retrieving revision 1.38 diff -u -p -r1.38 snmpc.c --- snmpc.c 21 Oct 2021 08:17:34 - 1.38 +++ snmpc.c 20 Dec 2022 10:33:58 - @@ -468,7 +468,9 @@ main(int argc, char *argv[]) argc -= optind; argv += optind; - if (version == SNMP_V1 || version == SNMP_V2C) { + if (!snmp_app->usecommonopt) { + /* No SNMP protocol settings */ + } else if (version == SNMP_V1 || version == SNMP_V2C) { if (community == NULL || community[0] == '\0') errx(1, "No community name specified."); } else if (version == SNMP_V3) {
Re: 7.2: snmp mibtree command broken
On Tue, 2022-12-20 at 10:21 +0100, Matthias Pitzl wrote: > Hi, > > Since the release of OpenBSD 7.2, snmp mibtree is broken: > > root@host:~# snmp mibtree > > snmp: No securityName specified > > Greetings, > Matthias Apparently no one has used this one in a long time (including me). This got broken in snmpc.c r1.37 on 2021/08/11 when changing snmp(1)'s default to v3. This may not the prettiest solution, but this is the shortest I can come up with. martijn@ Index: snmpc.c === RCS file: /cvs/src/usr.bin/snmp/snmpc.c,v retrieving revision 1.38 diff -u -p -r1.38 snmpc.c --- snmpc.c 21 Oct 2021 08:17:34 - 1.38 +++ snmpc.c 20 Dec 2022 10:08:29 - @@ -468,7 +468,9 @@ main(int argc, char *argv[]) argc -= optind; argv += optind; - if (version == SNMP_V1 || version == SNMP_V2C) { + if (strcmp(snmp_app->name, "mibtree") == 0) { + /* No SNMP protocol settings */ + } else if (version == SNMP_V1 || version == SNMP_V2C) { if (community == NULL || community[0] == '\0') errx(1, "No community name specified."); } else if (version == SNMP_V3) {
tcpdump(8): grok Alexey Kuznetzov's modified libpcap format.
I received a pcap file from someone containing kuznetzov's pcap format. According to "upstream" libpcap[0] this format uses struct pcap_sf_patched_pkthdr, which is: struct pcap_sf_patched_pkthdr { struct pcap_timeval ts; /* time stamp */ bpf_u_int32 caplen; /* length of portion present */ bpf_u_int32 len;/* length of this packet (off wire) */ int index; unsigned short protocol; unsigned char pkt_type; }; So compared to a normal pkthdr this adds[1]: - index: interface index - protocol: ethernet packet type - pkt_type: broadcast/multicast/etc. indication I don't think this information is particularly useful per say, so why not just skip the 8 bytes and continue business as usual? No public header change and allows me read the dump. OK? martijn@ [0] https://www.tcpdump.org/ [1] https://wiki.wireshark.org/Development/LibpcapFileFormat#modified-pcap Index: usr.sbin/tcpdump/privsep.h === RCS file: /cvs/src/usr.sbin/tcpdump/privsep.h,v retrieving revision 1.12 diff -u -p -r1.12 privsep.h --- usr.sbin/tcpdump/privsep.h 18 Mar 2019 00:09:22 - 1.12 +++ usr.sbin/tcpdump/privsep.h 1 Dec 2022 19:53:47 - @@ -19,8 +19,6 @@ #include -#define TCPDUMP_MAGIC 0xa1b2c3d4 - enum cmd_types { PRIV_OPEN_BPF, /* open a bpf descriptor */ PRIV_OPEN_DUMP, /* open dump file for reading */ Index: usr.sbin/tcpdump/privsep_pcap.c === RCS file: /cvs/src/usr.sbin/tcpdump/privsep_pcap.c,v retrieving revision 1.25 diff -u -p -r1.25 privsep_pcap.c --- usr.sbin/tcpdump/privsep_pcap.c 28 Jun 2019 13:32:51 - 1.25 +++ usr.sbin/tcpdump/privsep_pcap.c 1 Dec 2022 19:53:47 - @@ -334,6 +334,7 @@ priv_pcap_live(const char *dev, int slen static void swap_hdr(struct pcap_file_header *hp) { + hp->magic = swap32(hp->magic); hp->version_major = swap16(hp->version_major); hp->version_minor = swap16(hp->version_minor); hp->thiszone = swap32(hp->thiszone); @@ -390,20 +391,25 @@ priv_pcap_offline(const char *fname, cha goto bad; } - if (hdr.magic != TCPDUMP_MAGIC) { - if (swap32(hdr.magic) != TCPDUMP_MAGIC) { - snprintf(errbuf, PCAP_ERRBUF_SIZE, - "bad dump file format"); - goto bad; - } + switch (hdr.magic) { + case TCPDUMP_MAGIC: + case TCPDUMP_MAGIC_KUZNETZOV: + break; + case swap32(TCPDUMP_MAGIC): + case swap32(TCPDUMP_MAGIC_KUZNETZOV): p->sf.swapped = 1; swap_hdr(&hdr); + break; + default: + snprintf(errbuf, PCAP_ERRBUF_SIZE, "bad dump file format"); + goto bad; } if (hdr.version_major < PCAP_VERSION_MAJOR) { snprintf(errbuf, PCAP_ERRBUF_SIZE, "archaic file format"); goto bad; } + p->sf.magic = hdr.magic; p->tzoff = hdr.thiszone; p->snapshot = hdr.snaplen; p->linktype = hdr.linktype; Index: lib/libpcap/pcap-int.h === RCS file: /cvs/src/lib/libpcap/pcap-int.h,v retrieving revision 1.14 diff -u -p -r1.14 pcap-int.h --- lib/libpcap/pcap-int.h 5 Apr 2018 03:47:27 - 1.14 +++ lib/libpcap/pcap-int.h 1 Dec 2022 19:53:47 - @@ -51,6 +51,9 @@ struct pcap_opt { int immediate; /* immediate mode - deliver packets as soon as they arrive */ }; +#define TCPDUMP_MAGIC 0xa1b2c3d4 +#define TCPDUMP_MAGIC_KUZNETZOV 0xa1b2cd34 + /* * Savefile */ @@ -59,6 +62,7 @@ struct pcap_sf { int swapped; int version_major; int version_minor; + uint32_t magic; u_char *base; }; Index: lib/libpcap/savefile.c === RCS file: /cvs/src/lib/libpcap/savefile.c,v retrieving revision 1.17 diff -u -p -r1.17 savefile.c --- lib/libpcap/savefile.c 27 May 2020 04:24:01 - 1.17 +++ lib/libpcap/savefile.c 1 Dec 2022 19:53:47 - @@ -45,8 +45,6 @@ #include "pcap-int.h" -#define TCPDUMP_MAGIC 0xa1b2c3d4 - /* * We use the "receiver-makes-right" approach to byte order, * because time is at a premium when we are writing the file. @@ -90,6 +88,7 @@ sf_write_header(FILE *fp, int linktype, static void swap_hdr(struct pcap_file_header *hp) { + hp->magic = SWAPLONG(hp->magic); hp->version_major = SWAPSHORT(hp->version_major); hp->version_minor = SWAPSHORT(hp->version_minor); hp->thiszone = SWAPLONG(hp->thiszone); @@ -145,19 +144,24 @@ pcap_fopen_offline(FILE *fp, char *errbu pcap_strerror(errno)); goto bad; } - if (hdr.magic != TCPDUMP_MAGIC) { - if
Re: netstart(8): remove sed
On Wed, 2022-11-23 at 10:03 +, Klemens Nanni wrote: > On Wed, Nov 23, 2022 at 10:48:22AM +0100, Martijn van Duren wrote: > > On Wed, 2022-11-23 at 09:25 +, Klemens Nanni wrote: > > > On Wed, Nov 23, 2022 at 10:15:20AM +0100, Martijn van Duren wrote: > > > > Here's an attempt to remove sed from netstart. > > > > > > I don't see the point in this. > > > > On Mon, 2022-11-21 at 20:42 -0700, Theo de Raadt wrote: > > > Oh, except that using grep, head, or awk. That breaks NFS diskless > > > machines, because they are in /usr, which may not be mounted yet. > > > > > > So this has to be done using shell features or commands in /bin and /sbin. > > > You will see these interesting hacks in other parts of rc and netstart. > > > > > > > > > > https://marc.info/?l=openbsd-misc&m=166908823920806&w=2 > > Ah, NFS /usr. > > Instead of rolling our own shell functions, maybe this is a good reason > to implement ${parameter/pattern/string} in ksh(1)? > > This should make shell scripting in base a lot easier where awk/sed is > not available; the installer could probably use this as well. > > At least Bash has this (and other pattern substitution) syntax. > Maybe... I'm not a big fan of feature-bloat in the shell and this function is small enough, so personally I see no reason. But if you write it and no one else objects why not...
Re: netstart(8): remove sed
On Wed, 2022-11-23 at 09:25 +, Klemens Nanni wrote: > On Wed, Nov 23, 2022 at 10:15:20AM +0100, Martijn van Duren wrote: > > Here's an attempt to remove sed from netstart. > > I don't see the point in this. On Mon, 2022-11-21 at 20:42 -0700, Theo de Raadt wrote: > Oh, except that using grep, head, or awk. That breaks NFS diskless > machines, because they are in /usr, which may not be mounted yet. > > So this has to be done using shell features or commands in /bin and /sbin. > You will see these interesting hacks in other parts of rc and netstart. > > https://marc.info/?l=openbsd-misc&m=166908823920806&w=2
netstart(8): remove sed
Here's an attempt to remove sed from netstart. Since we use sed in a simple string replacement without any fancy regex stuff I think we can relatively easy use something based on shell built-ins. Risk of the current code is that if someone places search inside replacement we get an infinite loop, but since both search and replacement are under our control I don't think it's worth making the code larger than needed. Only lightly tested thoughts? martijn@ Index: netstart === RCS file: /cvs/src/etc/netstart,v retrieving revision 1.229 diff -u -p -r1.229 netstart --- netstart5 Nov 2022 12:06:05 - 1.229 +++ netstart23 Nov 2022 09:12:28 - @@ -35,6 +35,18 @@ stripcom() { done <$_file } +# Usage: gsub search replace line +gsub() { + local _s="$1" _r="$2" _l + shift 2 + _l="$@" + + while [[ "$_l" == *$_s* ]]; do + _l="${_l%%$_s*}$_r${_l#*$_s}" + done + print -n -- "$_l" +} + # Parse and "unpack" a hostname.if(5) line given as positional parameters. # Fill the _cmds array with the resulting interface configuration commands. parse_hn_line() { @@ -82,7 +94,7 @@ parse_hn_line() { dhcp) _cmds[${#_cmds[*]}]="ifconfig $_if inet autoconf" V4_AUTOCONF=true ;; - '!'*) _cmd=$(print -- "${_c[@]}" | sed 's/\$if/'$_if'/g') + '!'*) _cmd=$(gsub '$if' $_if "${_c[*]}") _cmds[${#_cmds[*]}]="${_cmd#!}" ;; *) _cmds[${#_cmds[*]}]="ifconfig $_if ${_c[@]}"
Re: lladdr support for netstart/hostname.if
On Tue, 2022-11-22 at 11:25 +0100, Claudio Jeker wrote: > On Tue, Nov 22, 2022 at 09:25:08AM +, Stuart Henderson wrote: > > Need to query (and set $if, which might be used in route commands etc) I > > think. > > > > I would prefer if people took a step back from configuring interfaces by > MAC address. It feels like a overly specific hack is introduced for > a case that should be handled in a different way. > > Not all interfaces have MAC addresses. E.g. umb(4) would need a > different identifier (most probably the IMEI). Some interfaces inherit > the MAC address from an other interface (vlan, trunk). > > This requires the use of interface groups to 'rename' these interfaces > e.g. as 'green' and 'blue' or 'in' and 'out'. So that you can use these > handles in pf.conf and other commands (rad comes to mind). Not all > commands work with interface groups. route(8) is such an example but there > are more commands needing this. > > Btw. a lot of this can be done already now by using '!' in hostname.if > It wont be pretty but it is also not a common use case. Maybe you're right, but as theo said doing it via !-commands is just horrendous. Best I can think of is letting people write their own script and call it via `!/path/to/script $if`, which is guaranteed going to end messy. Here's a completely other idea that might be more generic and hopefully doesn't cause this same level of confusion. What if instead of adding a new extension we let people do that themselves and add support for including other files? Diff below does this and must still be considered a PoC. And yes I know sed, which isn't allowed. But the command above it does the same thing, so I'm not going to apologise for it in a PoC. One downside in the current parse_hn_line format is that spaces other than a single SP are completely butchered, but I'm not expecting much problems there. In addition to $if which is available for '!'-commands I also added support for $lladdr. $ cat /etc/hostname.em0 . /etc/hostname.$lladdr $ cat /etc/hostname.88\:a4\:c2\:fb\:84\:77 autoconf up $ doas sh ./netstart -n em0 { ifconfig em0 || ifconfig em0 create; } ifconfig em0 autoconf ifconfig em0 up martijn@ Index: netstart === RCS file: /cvs/src/etc/netstart,v retrieving revision 1.229 diff -u -p -r1.229 netstart --- netstart5 Nov 2022 12:06:05 - 1.229 +++ netstart23 Nov 2022 07:22:07 - @@ -35,10 +35,38 @@ stripcom() { done <$_file } +LLGLOB='[[:xdigit:]][[:xdigit:]]:[[:xdigit:]][[:xdigit:]]' +LLGLOB="$LLGLOB:$LLGLOB:$LLGLOB" +set -A LLADDR_MAP -- $( + ifconfig | while IFS= read -- _line; do + if [[ "$_line" = +([[:alpha:]])+([[:digit:]]):* ]]; then + _if=${_line%:*} + elif [[ -n "$_if" +&& "$_line" = +([[:space:]])lladdr\ $LLGLOB ]]; then + print "$_if,${_line#*lladdr }" + _if= + fi + done +) + +# Find lladdr for if +# Usage: if2lladdr if1 +if2lladdr() { + local _if=$1 + for m in "${LLADDR_MAP[@]}"; do + if [[ "$_if" = "${m%,*}" ]]; then + print -- "${m#*,}" + break + fi + done + return 0 +} + # Parse and "unpack" a hostname.if(5) line given as positional parameters. # Fill the _cmds array with the resulting interface configuration commands. parse_hn_line() { - local _af=0 _name=1 _mask=2 _bc=3 _prefix=2 _c _cmd _prev _daddr _dhcp _i + local _af=0 _name=1 _mask=2 _bc=3 _prefix=2 _c _cmd _prev _daddr _dhcp + local _i _file _line set -A _c -- "$@" set -o noglob @@ -84,6 +112,17 @@ parse_hn_line() { ;; '!'*) _cmd=$(print -- "${_c[@]}" | sed 's/\$if/'$_if'/g') _cmds[${#_cmds[*]}]="${_cmd#!}" + ;; + '.')unset _c[_af] + _file="$(print -- "${_c[@]}" | sed -e 's/\$if/'$_if'/g' \ + -e 's/\$lladdr/'$(if2lladdr $_if)'/g')" + if [[ ! -f $_file ]]; then + print -u2 "${0##*/}: $_file: No such file or directory." + return + fi + while IFS= read -- _line; do + parse_hn_line $_line + done<"$_file" ;; *) _cmds[${#_cmds[*]}]="ifconfig $_if ${_c[@]}" ;;
lladdr support for netstart/hostname.if (was: Re: Locking network card configuration)
On Sun, 2022-11-20 at 19:35 -0700, Theo de Raadt wrote: > Steve Litt wrote: > > > Vitaliy Makkoveev said on Mon, 21 Nov 2022 03:48:21 +0300 > > > > > > On 20 Nov 2022, at 18:06, Odd Martin Baanrud > > > > wrote: > > > > > > > > Hello, > > > > > > > > I have a Raspberry Pi 4 with 2 USB NIC’s attached. > > > > One via USB3 (ure0), and the other via USB2 (ure1). > > > > Since they are connected to different USB interfaces, I thaught they > > > > would get configured the same way on reboot. But that’s not the case. > > > > They became swapped on reboot. > > > > Is there a way to “lock” the configuration I want? > > > > So the USB3 NIC always become ure0, and the USB2 ure1. > > > > > > > > Regards, Martin > > > > > > > > > > You could parse ifconfig(8) output to determine which names network > > > interfaces received. But unfortunately, you can’t rename interfaces. > > > > During your parsing you could assign each one to an environment > > variable such that, for instance, $lan contains the network card name > > of the LAN one, and $wan contains the network name of the one going to > > the Internet. Unfortunately, this would probably mean changing a lot of > > existing shellscripts, but it's doable. > > But that is not the problem. > > hostname.* installs addresses on an interface, based upon the name of that > interface. > > So it is too late for what you suggest. > > Unless the suggestion is have each hostname.* do a !command to a script which > does the assigning. That is pretty crazy. > > pf.conf is not the problem either, because that can be entirely written using > egress and groups. > > > > There is a problem with device attachment -> naming a device at that > moment -> using that name in netstart.. but I am not sure how we could > solve this without creating bigger problems for everyone else in the > other non-hot-plug configurations, which is the majority of users with > > 1 network device. > > We also hit this problem with disks, and we worked around it with the > DUID subsystem. > > > I suppose there is some argument that we should support hostname.MAC > files > I don't have a usecase for this myself, but it seemed like a nice exercise and might get the ball rolling. I also don't have much experience with our rc/netstart shellcode, so I'm expecting this diff should be taken as a starting-point iff we want something like this. I've chosen to error out on missing lladdr, duplicate lladdr and when there's a hostname.if for both the lladdr and the if variant. This means that there's smaller chance for order confusion or doubly applied configs. Downside is that if someone decided to backup their hostname.if to hostname.lladdr that will break their setup. However, I don't expect people to backup their config files in this manner, but you never know. Errors: On duplicate lladdr (in this case em0 and iwx0 in trunk0): $ doas sh /usr/src/etc/netstart 88:a4:c2:fb:84:77 netstart: /etc/hostname.88:a4:c2:fb:84:77: unique if for lladdr not found. On missing lladdr: $ doas sh /usr/src/etc/netstart 88:a4:c2:fb:84:76 netstart: /etc/hostname.88:a4:c2:fb:84:76: unique if for lladdr not found. And having both hostname.if and hostname.lladdr installed: $ doas sh ./netstart 00:11:22:33:44:55 netstart: /etc/hostname.00:11:22:33:44:55: duplicate config found in /etc/hostname.vio0. $ doas sh ./netstart vio0 netstart: /etc/hostname.vio0: duplicate config found in /etc/hostname.00:11:22:33:44:55. Two omissions I considered but didn't implement: 1) I didn't test if the lladdr is owned by one of `ifconfig -C` interfaces. Not sure if this is an upside or downside. 2) Allowing /etc/netstart if1 and parsing the hostname.lladdr1 and vice versa. martijn@ Index: etc/netstart === RCS file: /cvs/src/etc/netstart,v retrieving revision 1.219 diff -u -p -r1.219 netstart --- etc/netstart3 Jul 2022 12:14:36 - 1.219 +++ etc/netstart21 Nov 2022 15:40:23 - @@ -92,6 +92,40 @@ parse_hn_line() { set +o noglob } +# Find if for lladdr +# Usage: lladdr2if xx:xx:xx:xx:xx:xx +# Duplicate lladdrs result in error. +LLADDR_GLOB='[[:alnum:]][[:alnum:]]:[[:alnum:]][[:alnum:]]:' +LLADDR_GLOB=$LLADDR_GLOB'[[:alnum:]][[:alnum:]]:[[:alnum:]][[:alnum:]]:' +LLADDR_GLOB=$LLADDR_GLOB'[[:alnum:]][[:alnum:]]:[[:alnum:]][[:alnum:]]' +lladdr2if() { + local _line _if _lladdr="$1" + + _if="$(ifconfig | while IFS= read -- _line; do + [[ -n "${_line%%[[:space:]]*}" ]] && _if="${_line%%:*}" + if [[ -z "${_line%%[[:space:]]lladdr $LLADDR_GLOB}" && \ + "${_line##* }" = "$_lladdr" ]]; then + print -n -- "$_if " + fi + done)" + [[ -z "$_if" || -n "${_if#* }" ]] && return 1 + print -- $_if +} + +# Find lladdr for if +# Usage: if2lladdr if1 +if2lladdr() { + local _line _if="$1" + + ifconfig $_if 2>/d
Re: ber.c: Fix some minor issues in ober_read_element()
On Wed, 2022-11-02 at 18:34 +0100, Claudio Jeker wrote: > On Wed, Nov 02, 2022 at 05:56:21PM +0100, Martijn van Duren wrote: > > On Wed, 2022-11-02 at 17:47 +0100, Claudio Jeker wrote: > > > On Wed, Nov 02, 2022 at 05:25:12PM +0100, Martijn van Duren wrote: > > > > On Wed, 2022-11-02 at 17:00 +0100, Claudio Jeker wrote: > > > > > On Wed, Nov 02, 2022 at 07:33:14AM +0100, Martijn van Duren wrote: > > > > > > I found 2 minor issues in the handling of sequences/sets in > > > > > > ober_read_element(): > > > > > > 1) An empty sequence/set (which is basically always) unconditionally > > > > > >creates an (uninitialised) sub-element. Add the same length check > > > > > >used to check the next element > > > > > > 2) For each sub-element r is only checked for -1, but not if it > > > > > >overflows the length of the sequence itself. This is not a big > > > > > > risk > > > > > >since each sub-element is length-checked against the buffer > > > > > >availability and simply returns an ECANCELED, which would be no > > > > > >worse memory-wise than sending an extremely large packet. > > > > > > > > > > > > While here, only having the NULL of the comparison on the next line > > > > > > annoyed me. > > > > > > > > > > > > OK? > > > > > > > > > > See below. > > > > > > > > > > > martijn@ > > > > > > > > > > > > Index: ber.c > > > > > > === > > > > > > RCS file: /cvs/src/lib/libutil/ber.c,v > > > > > > retrieving revision 1.23 > > > > > > diff -u -p -r1.23 ber.c > > > > > > --- ber.c 21 Oct 2021 08:17:33 - 1.23 > > > > > > +++ ber.c 2 Nov 2022 06:28:33 - > > > > > > @@ -1375,7 +1375,7 @@ ober_read_element(struct ber *ber, struc > > > > > > break; > > > > > > case BER_TYPE_SEQUENCE: > > > > > > case BER_TYPE_SET: > > > > > > - if (elm->be_sub == NULL) { > > > > > > + if (len > 0 && elm->be_sub == NULL) { > > > > > > if ((elm->be_sub = ober_get_element(0)) == NULL) > > > > > > return -1; > > > > > > } > > > > > > > > > > OK, be_sub == NULL is something checked and better than an empty > > > > > object. > > > > > > > > I'm not sure I understand what you mean here. > > > > > > I just wanted to say that elm->be_sub == NULL is a condition that most > > > other code handles well. While having an empty element in elm->be_sub is > > > something other code may not handle well. > > > > Ah ok, so confirming that the change is OK. Thanks. > > > > > > > > > > > > > > @@ -1390,13 +1390,21 @@ ober_read_element(struct ber *ber, struc > > > > > > return -1; > > > > > > } > > > > > > r = ober_read_element(ber, next); > > > > > > - if (r == -1) > > > > > > + if (r == -1) { > > > > > > + /* sub-element overflows sequence/set */ > > > > > > > > > > This comment is not quite right. I think the proper comment here is: > > > > > > > > I'm not sure. Yes the sub-element overflows the buffer, so it is more > > > > accurate in the literal sense. However, since the sequence/set already > > > > has its entire length in the buffer it implies that an ECANCELED is an > > > > overflow of the sub-element of the sequence and therefore makes it > > > > clearer why the errno is overwritten. > > > > > > Actually why do we need to reset the errno anyway? > > > Is anything in userland looking at ECANCELED and retries after receiving > > > more data? > > > > Yes, aldap.c and snmpe.c do this. And even if it didn't, going with > > EINVAL is more concise than ECANCELED. > > I really dislike such errno magic but fixing that is a much bigger > project
Re: ber.c: Fix some minor issues in ober_read_element()
On Wed, 2022-11-02 at 17:47 +0100, Claudio Jeker wrote: > On Wed, Nov 02, 2022 at 05:25:12PM +0100, Martijn van Duren wrote: > > On Wed, 2022-11-02 at 17:00 +0100, Claudio Jeker wrote: > > > On Wed, Nov 02, 2022 at 07:33:14AM +0100, Martijn van Duren wrote: > > > > I found 2 minor issues in the handling of sequences/sets in > > > > ober_read_element(): > > > > 1) An empty sequence/set (which is basically always) unconditionally > > > >creates an (uninitialised) sub-element. Add the same length check > > > >used to check the next element > > > > 2) For each sub-element r is only checked for -1, but not if it > > > >overflows the length of the sequence itself. This is not a big risk > > > >since each sub-element is length-checked against the buffer > > > >availability and simply returns an ECANCELED, which would be no > > > >worse memory-wise than sending an extremely large packet. > > > > > > > > While here, only having the NULL of the comparison on the next line > > > > annoyed me. > > > > > > > > OK? > > > > > > See below. > > > > > > > martijn@ > > > > > > > > Index: ber.c > > > > === > > > > RCS file: /cvs/src/lib/libutil/ber.c,v > > > > retrieving revision 1.23 > > > > diff -u -p -r1.23 ber.c > > > > --- ber.c 21 Oct 2021 08:17:33 - 1.23 > > > > +++ ber.c 2 Nov 2022 06:28:33 - > > > > @@ -1375,7 +1375,7 @@ ober_read_element(struct ber *ber, struc > > > > break; > > > > case BER_TYPE_SEQUENCE: > > > > case BER_TYPE_SET: > > > > - if (elm->be_sub == NULL) { > > > > + if (len > 0 && elm->be_sub == NULL) { > > > > if ((elm->be_sub = ober_get_element(0)) == NULL) > > > > return -1; > > > > } > > > > > > OK, be_sub == NULL is something checked and better than an empty object. > > > > I'm not sure I understand what you mean here. > > I just wanted to say that elm->be_sub == NULL is a condition that most > other code handles well. While having an empty element in elm->be_sub is > something other code may not handle well. Ah ok, so confirming that the change is OK. Thanks. > > > > > > > > @@ -1390,13 +1390,21 @@ ober_read_element(struct ber *ber, struc > > > > return -1; > > > > } > > > > r = ober_read_element(ber, next); > > > > - if (r == -1) > > > > + if (r == -1) { > > > > + /* sub-element overflows sequence/set */ > > > > > > This comment is not quite right. I think the proper comment here is: > > > > I'm not sure. Yes the sub-element overflows the buffer, so it is more > > accurate in the literal sense. However, since the sequence/set already > > has its entire length in the buffer it implies that an ECANCELED is an > > overflow of the sub-element of the sequence and therefore makes it > > clearer why the errno is overwritten. > > Actually why do we need to reset the errno anyway? > Is anything in userland looking at ECANCELED and retries after receiving > more data? Yes, aldap.c and snmpe.c do this. And even if it didn't, going with EINVAL is more concise than ECANCELED. So is the comment OK as is, do you still want to go with your comment, or do you want to suggest something else? > > > > > > > /* sub-element overflows buffer */ > > > > + if (errno == ECANCELED) > > > > + errno = EINVAL; > > > > return -1; > > > > + } > > > > + if (r > len) { > > > > > > This check here is actually checking that the sub_element does not > > > overflow the sequence/set. So maybe move the abcve comment down here. > > > > I think it does exactly the same thing, with the only difference that > > the ECANCELED case didn't reside fully in the buffer. The reason I > > didn't add the comment here was becaus
Re: ber.c: Fix some minor issues in ober_read_element()
On Wed, 2022-11-02 at 17:00 +0100, Claudio Jeker wrote: > On Wed, Nov 02, 2022 at 07:33:14AM +0100, Martijn van Duren wrote: > > I found 2 minor issues in the handling of sequences/sets in > > ober_read_element(): > > 1) An empty sequence/set (which is basically always) unconditionally > >creates an (uninitialised) sub-element. Add the same length check > >used to check the next element > > 2) For each sub-element r is only checked for -1, but not if it > >overflows the length of the sequence itself. This is not a big risk > >since each sub-element is length-checked against the buffer > >availability and simply returns an ECANCELED, which would be no > >worse memory-wise than sending an extremely large packet. > > > > While here, only having the NULL of the comparison on the next line > > annoyed me. > > > > OK? > > See below. > > > martijn@ > > > > Index: ber.c > > === > > RCS file: /cvs/src/lib/libutil/ber.c,v > > retrieving revision 1.23 > > diff -u -p -r1.23 ber.c > > --- ber.c 21 Oct 2021 08:17:33 - 1.23 > > +++ ber.c 2 Nov 2022 06:28:33 - > > @@ -1375,7 +1375,7 @@ ober_read_element(struct ber *ber, struc > > break; > > case BER_TYPE_SEQUENCE: > > case BER_TYPE_SET: > > - if (elm->be_sub == NULL) { > > + if (len > 0 && elm->be_sub == NULL) { > > if ((elm->be_sub = ober_get_element(0)) == NULL) > > return -1; > > } > > OK, be_sub == NULL is something checked and better than an empty object. I'm not sure I understand what you mean here. > > > @@ -1390,13 +1390,21 @@ ober_read_element(struct ber *ber, struc > > return -1; > > } > > r = ober_read_element(ber, next); > > - if (r == -1) > > + if (r == -1) { > > + /* sub-element overflows sequence/set */ > > This comment is not quite right. I think the proper comment here is: I'm not sure. Yes the sub-element overflows the buffer, so it is more accurate in the literal sense. However, since the sequence/set already has its entire length in the buffer it implies that an ECANCELED is an overflow of the sub-element of the sequence and therefore makes it clearer why the errno is overwritten. > > /* sub-element overflows buffer */ > > + if (errno == ECANCELED) > > + errno = EINVAL; > > return -1; > > + } > > + if (r > len) { > > This check here is actually checking that the sub_element does not > overflow the sequence/set. So maybe move the abcve comment down here. I think it does exactly the same thing, with the only difference that the ECANCELED case didn't reside fully in the buffer. The reason I didn't add the comment here was because I think it's clear from the context, where that context might be less obvious at ECANCELED. > > > + errno = EINVAL; > > + return -1; > > + } > > elements++; > > len -= r; > > if (len > 0 && next->be_next == NULL) { > > - if ((next->be_next = ober_get_element(0)) == > > - NULL) > > + next->be_next = ober_get_element(0); > > + if (next->be_next == NULL) > > return -1; > > } > > next = next->be_next; > > > > Apart from that OK claudio@ >
ber.c: Fix some minor issues in ober_read_element()
I found 2 minor issues in the handling of sequences/sets in ober_read_element(): 1) An empty sequence/set (which is basically always) unconditionally creates an (uninitialised) sub-element. Add the same length check used to check the next element 2) For each sub-element r is only checked for -1, but not if it overflows the length of the sequence itself. This is not a big risk since each sub-element is length-checked against the buffer availability and simply returns an ECANCELED, which would be no worse memory-wise than sending an extremely large packet. While here, only having the NULL of the comparison on the next line annoyed me. OK? martijn@ Index: ber.c === RCS file: /cvs/src/lib/libutil/ber.c,v retrieving revision 1.23 diff -u -p -r1.23 ber.c --- ber.c 21 Oct 2021 08:17:33 - 1.23 +++ ber.c 2 Nov 2022 06:28:33 - @@ -1375,7 +1375,7 @@ ober_read_element(struct ber *ber, struc break; case BER_TYPE_SEQUENCE: case BER_TYPE_SET: - if (elm->be_sub == NULL) { + if (len > 0 && elm->be_sub == NULL) { if ((elm->be_sub = ober_get_element(0)) == NULL) return -1; } @@ -1390,13 +1390,21 @@ ober_read_element(struct ber *ber, struc return -1; } r = ober_read_element(ber, next); - if (r == -1) + if (r == -1) { + /* sub-element overflows sequence/set */ + if (errno == ECANCELED) + errno = EINVAL; return -1; + } + if (r > len) { + errno = EINVAL; + return -1; + } elements++; len -= r; if (len > 0 && next->be_next == NULL) { - if ((next->be_next = ober_get_element(0)) == - NULL) + next->be_next = ober_get_element(0); + if (next->be_next == NULL) return -1; } next = next->be_next;
Re: snmpd_metrics: Don't stop walking on empty table
ping On Wed, 2022-10-26 at 10:45 +0200, Martijn van Duren wrote: > Found by Alec on misc@. > When there's an empty table pfta_get_nextaddr jumps to fail and > mib.c returns an agentx_varbind_notfound not libagentx, resulting > in the upper code assuming that the object has no more entries > after that. Note that this is not just an issue of the new code, > but apparently no one ever noticed. > > I see no risk at removing the ba.pfrb_size == 0 check, since > PFRB_FOREACH calls pfr_buf_next which does that check as well. > > Alec already confirmed that this diff works for him. > > OK? > > martijn@ > Index: pf.c === RCS file: /cvs/src/libexec/snmpd/snmpd_metrics/pf.c,v retrieving revision 1.1.1.1 diff -u -p -r1.1.1.1 pf.c --- pf.c1 Sep 2022 14:20:33 - 1.1.1.1 +++ pf.c26 Oct 2022 08:44:27 - @@ -418,7 +418,7 @@ pfta_get_nextaddr(struct pfr_astats *ras sizeof(filter.pfrt_name)) >= sizeof(filter.pfrt_name)) goto fail; - if (pfta_get(&ba, &filter) || ba.pfrb_size == 0) + if (pfta_get(&ba, &filter)) goto fail; PFRB_FOREACH(as, &ba) {
Re: snmpd_metrics: Don't stop walking on empty table
I should've probably been more clear: This is about OPENBSD-PF-MIB's pfTblAddrTable. On Wed, 2022-10-26 at 10:45 +0200, Martijn van Duren wrote: > Found by Alec on misc@. > When there's an empty table pfta_get_nextaddr jumps to fail and > mib.c returns an agentx_varbind_notfound not libagentx, resulting > in the upper code assuming that the object has no more entries > after that. Note that this is not just an issue of the new code, > but apparently no one ever noticed. > > I see no risk at removing the ba.pfrb_size == 0 check, since > PFRB_FOREACH calls pfr_buf_next which does that check as well. > > Alec already confirmed that this diff works for him. > > OK? > > martijn@ > > Index: pf.c > === > RCS file: /cvs/src/libexec/snmpd/snmpd_metrics/pf.c,v > retrieving revision 1.1.1.1 > diff -u -p -r1.1.1.1 pf.c > --- pf.c 1 Sep 2022 14:20:33 - 1.1.1.1 > +++ pf.c 26 Oct 2022 08:44:27 - > @@ -418,7 +418,7 @@ pfta_get_nextaddr(struct pfr_astats *ras > sizeof(filter.pfrt_name)) >= sizeof(filter.pfrt_name)) > goto fail; > > - if (pfta_get(&ba, &filter) || ba.pfrb_size == 0) > + if (pfta_get(&ba, &filter)) > goto fail; > > PFRB_FOREACH(as, &ba) { >
snmpd_metrics: Don't stop walking on empty table
Found by Alec on misc@. When there's an empty table pfta_get_nextaddr jumps to fail and mib.c returns an agentx_varbind_notfound not libagentx, resulting in the upper code assuming that the object has no more entries after that. Note that this is not just an issue of the new code, but apparently no one ever noticed. I see no risk at removing the ba.pfrb_size == 0 check, since PFRB_FOREACH calls pfr_buf_next which does that check as well. Alec already confirmed that this diff works for him. OK? martijn@ Index: pf.c === RCS file: /cvs/src/libexec/snmpd/snmpd_metrics/pf.c,v retrieving revision 1.1.1.1 diff -u -p -r1.1.1.1 pf.c --- pf.c1 Sep 2022 14:20:33 - 1.1.1.1 +++ pf.c26 Oct 2022 08:44:27 - @@ -418,7 +418,7 @@ pfta_get_nextaddr(struct pfr_astats *ras sizeof(filter.pfrt_name)) >= sizeof(filter.pfrt_name)) goto fail; - if (pfta_get(&ba, &filter) || ba.pfrb_size == 0) + if (pfta_get(&ba, &filter)) goto fail; PFRB_FOREACH(as, &ba) {
Fix description in OPENBSD-PF-MIB
As pointed out by Alec on misc@, there's a discrepancy between the name and description of several objects inside the pfIfTable. Looks like a simple copy-paste error. OK? martijn@ Index: OPENBSD-PF-MIB.txt === RCS file: /cvs/src/share/snmp/OPENBSD-PF-MIB.txt,v retrieving revision 1.7 diff -u -p -r1.7 OPENBSD-PF-MIB.txt --- OPENBSD-PF-MIB.txt 23 Mar 2021 19:37:51 - 1.7 +++ OPENBSD-PF-MIB.txt 19 Oct 2022 06:36:22 - @@ -36,7 +36,7 @@ IMPORTS FROM SNMPv2-CONF; pfMIBObjects MODULE-IDENTITY -LAST-UPDATED "202103231933Z" +LAST-UPDATED "202210190830Z" ORGANIZATION "OpenBSD" CONTACT-INFO " Author: Joel Knight @@ -46,6 +46,8 @@ pfMIBObjects MODULE-IDENTITY DESCRIPTION "The MIB module for gathering information from OpenBSD's packet filter. " +REVISION "202210190830Z" +DESCRIPTION "Fix description of several objects from bytes to the obviously intended packets" REVISION "202103231933Z" DESCRIPTION "Use DisplayString/SnmpAdminString not OCTET STRING where appropriate" REVISION "201506091728Z" @@ -794,7 +796,7 @@ pfIfOut4PassPkts OBJECT-TYPE MAX-ACCESS read-only STATUS current DESCRIPTION - "The number of IPv4 bytes passed out." + "The number of IPv4 packets passed out." ::= { pfIfEntry 10 } pfIfOut4PassBytes OBJECT-TYPE @@ -810,7 +812,7 @@ pfIfOut4BlockPkts OBJECT-TYPE MAX-ACCESS read-only STATUS current DESCRIPTION - "The number of outgoing IPv4 bytes blocked." + "The number of outgoing IPv4 packets blocked." ::= { pfIfEntry 12 } pfIfOut4BlockBytes OBJECT-TYPE @@ -858,7 +860,7 @@ pfIfOut6PassPkts OBJECT-TYPE MAX-ACCESS read-only STATUS current DESCRIPTION - "The number of IPv6 bytes passed out." + "The number of IPv6 packets passed out." ::= { pfIfEntry 18 } pfIfOut6PassBytes OBJECT-TYPE @@ -874,7 +876,7 @@ pfIfOut6BlockPkts OBJECT-TYPE MAX-ACCESS read-only STATUS current DESCRIPTION - "The number of outgoing IPv6 bytes blocked." + "The number of outgoing IPv6 packets blocked." ::= { pfIfEntry 20 } pfIfOut6BlockBytes OBJECT-TYPE
Re: snmpd(8): don't link to libkvm
On Fri, 2022-10-14 at 09:31 -0600, Theo de Raadt wrote: > Martijn van Duren wrote: > > > This one got overlooked when all the metrics moved to snmpd_metrics. > > > > OK? > > > > martijn@ > > > > Index: Makefile > > === > > RCS file: /cvs/src/usr.sbin/snmpd/Makefile,v > > retrieving revision 1.21 > > diff -u -p -r1.21 Makefile > > --- Makefile6 Oct 2022 14:41:08 - 1.21 > > +++ Makefile14 Oct 2022 15:28:48 - > > @@ -8,7 +8,7 @@ SRCS= parse.y log.c snmpe.c application > > mps.c trap.c mib.c smi.c snmpd.c \ > > proc.c usm.c traphandler.c util.c > > > > -LDADD= -levent -lutil -lkvm -lcrypto > > +LDADD= -levent -lutil -lcrypto > > DPADD= ${LIBEVENT} ${LIBUTIL} > > CFLAGS+= -Wall -I${.CURDIR} > > CFLAGS+= -Wstrict-prototypes -Wmissing-prototypes > > > > No kidding, but your DPADD dependency is also wrong. right... Index: Makefile === RCS file: /cvs/src/usr.sbin/snmpd/Makefile,v retrieving revision 1.21 diff -u -p -r1.21 Makefile --- Makefile6 Oct 2022 14:41:08 - 1.21 +++ Makefile14 Oct 2022 15:49:22 - @@ -8,8 +8,8 @@ SRCS= parse.y log.c snmpe.c application mps.c trap.c mib.c smi.c snmpd.c \ proc.c usm.c traphandler.c util.c -LDADD= -levent -lutil -lkvm -lcrypto -DPADD= ${LIBEVENT} ${LIBUTIL} +LDADD= -levent -lutil -lcrypto +DPADD= ${LIBEVENT} ${LIBUTIL} ${LIBCRYPTO} CFLAGS+= -Wall -I${.CURDIR} CFLAGS+= -Wstrict-prototypes -Wmissing-prototypes CFLAGS+= -Wmissing-declarations
snmpd(8): don't link to libkvm
This one got overlooked when all the metrics moved to snmpd_metrics. OK? martijn@ Index: Makefile === RCS file: /cvs/src/usr.sbin/snmpd/Makefile,v retrieving revision 1.21 diff -u -p -r1.21 Makefile --- Makefile6 Oct 2022 14:41:08 - 1.21 +++ Makefile14 Oct 2022 15:28:48 - @@ -8,7 +8,7 @@ SRCS= parse.y log.c snmpe.c application mps.c trap.c mib.c smi.c snmpd.c \ proc.c usm.c traphandler.c util.c -LDADD= -levent -lutil -lkvm -lcrypto +LDADD= -levent -lutil -lcrypto DPADD= ${LIBEVENT} ${LIBUTIL} CFLAGS+= -Wall -I${.CURDIR} CFLAGS+= -Wstrict-prototypes -Wmissing-prototypes
vmd: allow agentx to reconnect closed components
This builds on top of the previous two diffs. Easiest way to test is to add the following line to vm.conf in combination with snmpd(8): agentx context foo This results in: [fd:6 sess:4009008336 ctx:foo]: region .1.3.6.1.2.1.236: opening [fd:6 sess:4009008336 ctx:foo]: region .1.3.6.1.2.1.236: Unsupported context If you then remove the "context foo" part and run vmctl reload vmd should properly register: [fd:6 sess:4009008336 ctx:]: region .1.3.6.1.2.1.236: opening [fd:6 sess:4009008336 ctx:]: region .1.3.6.1.2.1.236: open OK? martijn@ Index: vm_agentx.c === RCS file: /cvs/src/usr.sbin/vmd/vm_agentx.c,v retrieving revision 1.1 diff -u -p -r1.1 vm_agentx.c --- vm_agentx.c 13 Sep 2022 10:28:19 - 1.1 +++ vm_agentx.c 11 Oct 2022 18:52:45 - @@ -321,6 +321,7 @@ vm_agentx_configure(struct vmd_agentx *e changed = 1; } + agentx_retry(conn->agentx); if (!changed) return;
libagentx: Allow not enabled components to retry
This one is on top of the don't reset errors diff. This diff includes a minor bump. Don't know if we're still to close after unlock. If certain components (session, agentcaps, region, index, object) are in a closed state while they are expected to be open, there is no way to retry them without destroying the entire structure and rebuilding it from scratch. This diff adds agentx_retry, which walks the tree and retries all in state AX_CSTATE_CLOSE. This function could be triggered by the admin, e.g. on a daemon reload. OK? If so, now? martijn@ diff --git a/Symbols.list b/Symbols.list index 6eda2be..a5c8bf7 100644 --- a/Symbols.list +++ b/Symbols.list @@ -4,6 +4,7 @@ agentx_log_info agentx_log_debug agentx agentx_connect +agentx_retry agentx_read agentx_write agentx_wantwrite diff --git a/agentx.3 b/agentx.3 index d45a3ae..81322c6 100644 --- a/agentx.3 +++ b/agentx.3 @@ -24,6 +24,7 @@ .Nm agentx_log_debug , .Nm agentx , .Nm agentx_connect , +.Nm agentx_retry , .Nm agentx_read , .Nm agentx_write , .Nm agentx_wantwrite , @@ -95,6 +96,8 @@ .Ft void .Fn agentx_connect "struct agentx *sa" "int fd" .Ft void +.Fn agentx_retry "struct agentx *sa" +.Ft void .Fn agentx_read "struct agentx *sa" .Ft void .Fn agentx_write "struct agentx *sa" @@ -367,6 +370,12 @@ is ready for a write, the function .Fn agentx_write should be called. .Pp +If any of the session, agentcaps, region, index, or objects failed to enable +correctly +.Pq as can be seen by the admin through the logs +they can be retried through +.Fn agentx_retry. +.Pp .Fa sa can be freed via .Fn agentx_free . diff --git a/agentx.c b/agentx.c index 8df1032..5f4f382 100644 --- a/agentx.c +++ b/agentx.c @@ -133,6 +133,7 @@ void (*agentx_wantwrite)(struct agentx *, int) = agentx_wantwritenow; static void agentx_reset(struct agentx *); static void agentx_free_finalize(struct agentx *); +static int agentx_session_retry(struct agentx_session *); static int agentx_session_start(struct agentx_session *); static int agentx_session_finalize(struct ax_pdu *, void *); static int agentx_session_close(struct agentx_session *, @@ -140,6 +141,7 @@ static int agentx_session_close(struct agentx_session *, static int agentx_session_close_finalize(struct ax_pdu *, void *); static void agentx_session_free_finalize(struct agentx_session *); static void agentx_session_reset(struct agentx_session *); +static int agentx_context_retry(struct agentx_context *); static void agentx_context_start(struct agentx_context *); static void agentx_context_free_finalize(struct agentx_context *); static void agentx_context_reset(struct agentx_context *); @@ -149,6 +151,7 @@ static int agentx_agentcaps_close(struct agentx_agentcaps *); static int agentx_agentcaps_close_finalize(struct ax_pdu *, void *); static void agentx_agentcaps_free_finalize(struct agentx_agentcaps *); static void agentx_agentcaps_reset(struct agentx_agentcaps *); +static int agentx_region_retry(struct agentx_region *); static int agentx_region_start(struct agentx_region *); static int agentx_region_finalize(struct ax_pdu *, void *); static int agentx_region_close(struct agentx_region *); @@ -229,6 +232,25 @@ agentx_connect(struct agentx *ax, int fd) agentx_finalize(ax, fd); } +void +agentx_retry(struct agentx *ax) +{ + struct agentx_session *axs; + + if (ax->ax_fd == -1) + return; + + TAILQ_FOREACH(axs, &(ax->ax_sessions), axs_ax_sessions) { + if (axs->axs_cstate == AX_CSTATE_OPEN) { + if (agentx_session_retry(axs) == -1) + return; + } else if (axs->axs_cstate == AX_CSTATE_CLOSE) { + if (agentx_session_start(axs) == -1) + return; + } + } +} + static void agentx_start(struct agentx *ax) { @@ -401,6 +423,26 @@ agentx_session(struct agentx *ax, uint32_t oid[], return axs; } +static int +agentx_session_retry(struct agentx_session *axs) +{ + struct agentx_context *axc; + +#ifdef AX_DEBUG + if (axs->axs_cstate != AX_CSTATE_OPEN) + agentx_log_axs_fatalx(axs, "%s: unexpected retry", __func__); +#endif + + TAILQ_FOREACH(axc, &(axs->axs_contexts), axc_axs_contexts) { + if (axc->axc_cstate == AX_CSTATE_OPEN) { + if (agentx_context_retry(axc) == -1) + return -1; + } else if (axc->axc_cstate == AX_CSTATE_CLOSE) + agentx_context_start(axc); + } + return 0; +} + static int agentx_session_start(struct agentx_session *axs) { @@ -628,6 +670,36 @@ agentx_context(struct agentx_session *axs, const char *name) return axc; } +static int +agentx_context_retry(struct agentx_context *axc) +{ + struct agentx_agentcaps *axa; + struct agentx_region *axr; + +#ifdef AX_DEBUG + if (axc->axc_cstate != AX_CSTATE_OPEN) + a
libagentx: don't reset on server errors
There's a couple of cases in libagentx where we call agentx_reset on error cases returned by the server, which in turn results in the connection being closed and retried. There's no reason to expect the server to return another response on the next try. I think it's better to just keep the different structs in a closed state and just miss that particular part of the functionality until it's fixed. Found by explicitly setting vmd's agentx context, which is supported by net-snmpd, but not by snmpd(8), which just returns an error and creates an infinite loop. OK? martijn diff --git a/agentx.c b/agentx.c index 3ee05e6..8df1032 100644 --- a/agentx.c +++ b/agentx.c @@ -444,7 +444,7 @@ agentx_session_finalize(struct ax_pdu *pdu, void *cookie) if (pdu->ap_payload.ap_response.ap_error != AX_PDU_ERROR_NOERROR) { agentx_log_ax_warnx(ax, "failed to open session: %s", ax_error2string(pdu->ap_payload.ap_response.ap_error)); - agentx_reset(ax); + axs->axs_cstate = AX_CSTATE_CLOSE; return -1; } @@ -1112,8 +1112,6 @@ agentx_region_finalize(struct ax_pdu *pdu, void *cookie) { struct agentx_region *axr = cookie; struct agentx_context *axc = axr->axr_axc; - struct agentx_session *axs = axc->axc_axs; - struct agentx *ax = axs->axs_ax; struct agentx_index *axi; struct agentx_object *axo; @@ -1140,22 +1138,11 @@ agentx_region_finalize(struct ax_pdu *pdu, void *cookie) agentx_log_axc_info(axc, "region %s: duplicate, can't " "reduce priority, ignoring", ax_oid2string(&(axr->axr_oid))); - } else if (pdu->ap_payload.ap_response.ap_error == - AX_PDU_ERROR_REQUESTDENIED) { + } else { axr->axr_cstate = AX_CSTATE_CLOSE; agentx_log_axc_warnx(axc, "region %s: %s", ax_oid2string(&(axr->axr_oid)), ax_error2string(pdu->ap_payload.ap_response.ap_error)); - /* -* If we can't register a region, related objects are useless. -* But no need to retry. -*/ - return 0; - } else { - agentx_log_axc_info(axc, "region %s: %s", - ax_oid2string(&(axr->axr_oid)), - ax_error2string(pdu->ap_payload.ap_response.ap_error)); - agentx_reset(ax); return -1; } @@ -1648,8 +1635,6 @@ agentx_index_finalize(struct ax_pdu *pdu, void *cookie) struct agentx_index *axi = cookie; struct agentx_region *axr = axi->axi_axr; struct agentx_context *axc = axr->axr_axc; - struct agentx_session *axs = axc->axc_axs; - struct agentx *ax = axs->axs_ax; struct ax_pdu_response *resp; size_t i; @@ -1675,20 +1660,20 @@ agentx_index_finalize(struct ax_pdu *pdu, void *cookie) if (resp->ap_nvarbind != 1) { agentx_log_axc_warnx(axc, "index %s: unexpected number of " "indices", ax_oid2string(&(axr->axr_oid))); - agentx_reset(ax); + axi->axi_cstate = AX_CSTATE_CLOSE; return -1; } if (resp->ap_varbindlist[0].avb_type != axi->axi_vb.avb_type) { agentx_log_axc_warnx(axc, "index %s: unexpected index type", ax_oid2string(&(axr->axr_oid))); - agentx_reset(ax); + axi->axi_cstate = AX_CSTATE_CLOSE; return -1; } if (ax_oid_cmp(&(resp->ap_varbindlist[0].avb_oid), &(axi->axi_vb.avb_oid)) != 0) { agentx_log_axc_warnx(axc, "index %s: unexpected oid", ax_oid2string(&(axr->axr_oid))); - agentx_reset(ax); + axi->axi_cstate = AX_CSTATE_CLOSE; return -1; } @@ -1702,7 +1687,7 @@ agentx_index_finalize(struct ax_pdu *pdu, void *cookie) resp->ap_varbindlist[0].avb_data.avb_int32) { agentx_log_axc_warnx(axc, "index %s: unexpected " "index value", ax_oid2string(&(axr->axr_oid))); - agentx_reset(ax); + axi->axi_cstate = AX_CSTATE_CLOSE; return -1; } agentx_log_axc_info(axc, "index %s: allocated '%d'",
snmp: Add support for PF_LIMIT_ANCHORS
Just before lock mbuhl pointed out a new limit placed in pf, not exported yet over snmp. Here's a diff to add support for PF_LIMIT_ANCHORS. the OPENBSD-PF-MIB.txt DESCRIPTION is adapted from pfLimitMaxTables. The snmp{,d} parts are there just for pretty printing. OK? martijn@ Index: share/snmp/OPENBSD-PF-MIB.txt === RCS file: /cvs/src/share/snmp/OPENBSD-PF-MIB.txt,v retrieving revision 1.7 diff -u -p -r1.7 OPENBSD-PF-MIB.txt --- share/snmp/OPENBSD-PF-MIB.txt 23 Mar 2021 19:37:51 - 1.7 +++ share/snmp/OPENBSD-PF-MIB.txt 6 Oct 2022 16:14:32 - @@ -493,6 +493,14 @@ pfLimitMaxTableEntries OBJECT-TYPE tables." ::= { pfLimits 5 } +pfLimitAnchors OBJECT-TYPE + SYNTAX Unsigned32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The maximum number of anchors that can be created as part of the + active ruleset." + ::= { pfLimits 6 } -- pfTimeouts Index: usr.bin/snmp/mib.h === RCS file: /cvs/src/usr.bin/snmp/mib.h,v retrieving revision 1.10 diff -u -p -r1.10 mib.h --- usr.bin/snmp/mib.h 23 Mar 2021 22:05:21 - 1.10 +++ usr.bin/snmp/mib.h 6 Oct 2022 16:14:32 - @@ -580,6 +580,7 @@ #define MIB_pfLimitFragments MIB_pfLimits, 3 #define MIB_pfLimitMaxTables MIB_pfLimits, 4 #define MIB_pfLimitMaxTableEntries MIB_pfLimits, 5 +#define MIB_pfLimitAnchors MIB_pfLimits, 6 #define MIB_pfTimeouts MIB_pfMIBObjects, 7 #define MIB_pfTimeoutTcpFirst MIB_pfTimeouts, 1 #define MIB_pfTimeoutTcpOpeningMIB_pfTimeouts, 2 @@ -1217,6 +1218,7 @@ { MIBDECL(pfLimitFragments) }, \ { MIBDECL(pfLimitMaxTables) }, \ { MIBDECL(pfLimitMaxTableEntries) },\ + { MIBDECL(pfLimitAnchors) },\ { MIBDECL(pfTimeouts) },\ { MIBDECL(pfTimeoutTcpFirst) }, \ { MIBDECL(pfTimeoutTcpOpening) }, \ Index: usr.sbin/snmpd/mib.h === RCS file: /cvs/src/usr.sbin/snmpd/mib.h,v retrieving revision 1.41 diff -u -p -r1.41 mib.h --- usr.sbin/snmpd/mib.h19 Jan 2022 10:26:37 - 1.41 +++ usr.sbin/snmpd/mib.h6 Oct 2022 16:14:32 - @@ -550,6 +550,7 @@ #define MIB_pfLimitFragments MIB_pfLimits, 3 #define MIB_pfLimitMaxTables MIB_pfLimits, 4 #define MIB_pfLimitMaxTableEntries MIB_pfLimits, 5 +#define MIB_pfLimitAnchors MIB_pfLimits, 6 #define MIB_pfTimeouts MIB_pfMIBObjects, 7 #define MIB_pfTimeoutTcpFirst MIB_pfTimeouts, 1 #define MIB_pfTimeoutTcpOpeningMIB_pfTimeouts, 2 @@ -1126,6 +1127,7 @@ { MIBDECL(pfLimitFragments) }, \ { MIBDECL(pfLimitMaxTables) }, \ { MIBDECL(pfLimitMaxTableEntries) },\ + { MIBDECL(pfLimitAnchors) },\ { MIBDECL(pfTimeouts) },\ { MIBDECL(pfTimeoutTcpFirst) }, \ { MIBDECL(pfTimeoutTcpOpening) }, \ Index: libexec/snmpd/snmpd_metrics/mib.c === RCS file: /cvs/src/libexec/snmpd/snmpd_metrics/mib.c,v retrieving revision 1.1.1.1 diff -u -p -r1.1.1.1 mib.c --- libexec/snmpd/snmpd_metrics/mib.c 1 Sep 2022 14:20:34 - 1.1.1.1 +++ libexec/snmpd/snmpd_metrics/mib.c 6 Oct 2022 16:14:32 - @@ -146,6 +146,7 @@ struct agentx_object *pfSrcTrackCount, * struct agentx_object *pfSrcTrackRemovals; struct agentx_object *pfLimitStates, *pfLimitSourceNodes, *pfLimitFragments; struct agentx_object *pfLimitMaxTables, *pfLimitMaxTableEntries; +struct agentx_object *pfLimitAnchors; struct agentx_object *pfTimeoutTcpFirst, *pfTimeoutTcpOpening; struct agentx_object *pfTimeoutTcpEstablished, *pfTimeoutTcpClosing; struct agentx_object *pfTimeoutTcpFinWait, *pfTimeoutTcpClosed; @@ -1404,6 +1405,8 @@ mib_pflimits(struct agentx_varbind *vb) pl.index = PF_LIMIT_TABLES; else if (obj == pfLimitMaxTableEntries) pl.index = PF_LIMIT_TABLE_ENTRIES; + else if (obj == pfLimitAnchors) + pl.index = PF_LIMIT_ANCHORS; else fatal("%s: Unexpected object", __func__); @@ -3614,6 +3617,9 @@ main(int argc, char *argv[]) AGENTX_OID(PFLIMITMAXTABLES), NULL, 0, 0, mib_pflimits)) == NULL || (pfLimitMaxTableEntries = agentx_object(pfMIBObjects, AGENTX_OID(PFLIMITMAXTABLEENTRIES), NULL, 0, 0, + mib_pflimits)) == NULL || + (pfLimitAnchors = agentx_object(pfMIBObjects, + AGENTX_OID(PFLIMITANCHORS), NULL, 0, 0, mib_pflimits)
Re: Remove some unnecessary setproctitle(3) format strings
On Tue, 2022-09-27 at 11:23 +0200, Florian Obser wrote: > On 2022-09-27 09:26 +02, Martijn van Duren wrote: > > The caveats section talks about "user-supplied data". These string are > > constant and don't contain any '%'. Most other daemons in base use the > > setproctitle("title"); format as well. > > It's not that clear cut, snmpd(8), a daemon you might be familiar with, > uses > setproctitle("%s", p->p_title); > > while p->p_title is not user supplied ;) True, but proc.c doesn't know how p_title is filled :-) > > The diff is probably not wrong (I only glanced at it), but I don't think > it solves a problem. It then comes down to style and I prefer things the > way they are. I only made the remark in regards to the caveat argument. If it's personal preference, that's a good enough reason for me to keep it as is. Especially for daemons that I don't work on. I addressed the argument, not the conclusion. > > > > > On Tue, 2022-09-27 at 08:07 +0100, Stuart Henderson wrote: > > > These programs seem OK as-is, they are following the advice in > > > https://man.openbsd.org/setproctitle.3#CAVEATS >
Re: Remove some unnecessary setproctitle(3) format strings
The caveats section talks about "user-supplied data". These string are constant and don't contain any '%'. Most other daemons in base use the setproctitle("title"); format as well. On Tue, 2022-09-27 at 08:07 +0100, Stuart Henderson wrote: > These programs seem OK as-is, they are following the advice in > https://man.openbsd.org/setproctitle.3#CAVEATS > > On 2022/09/26 18:06, Josiah Frentsos wrote: > > Index: sbin/dhcpleased/engine.c > > === > > RCS file: /cvs/src/sbin/dhcpleased/engine.c,v > > retrieving revision 1.38 > > diff -u -p -r1.38 engine.c > > --- sbin/dhcpleased/engine.c5 May 2022 14:44:59 - 1.38 > > +++ sbin/dhcpleased/engine.c26 Sep 2022 21:43:28 - > > @@ -197,7 +197,7 @@ engine(int debug, int verbose) > > if (unveil(NULL, NULL) == -1) > > fatal("unveil"); > > > > - setproctitle("%s", "engine"); > > + setproctitle("engine"); > > log_procinit("engine"); > > > > if (setgroups(1, &pw->pw_gid) || > > Index: sbin/dhcpleased/frontend.c > > === > > RCS file: /cvs/src/sbin/dhcpleased/frontend.c,v > > retrieving revision 1.30 > > diff -u -p -r1.30 frontend.c > > --- sbin/dhcpleased/frontend.c 14 Jul 2022 15:23:09 - 1.30 > > +++ sbin/dhcpleased/frontend.c 26 Sep 2022 21:43:29 - > > @@ -151,7 +151,7 @@ frontend(int debug, int verbose) > > if (unveil(NULL, NULL) == -1) > > fatal("unveil"); > > > > - setproctitle("%s", "frontend"); > > + setproctitle("frontend"); > > log_procinit("frontend"); > > > > if ((ioctlsock = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0)) == -1) > > Index: sbin/slaacd/engine.c > > === > > RCS file: /cvs/src/sbin/slaacd/engine.c,v > > retrieving revision 1.84 > > diff -u -p -r1.84 engine.c > > --- sbin/slaacd/engine.c26 Aug 2022 00:02:08 - 1.84 > > +++ sbin/slaacd/engine.c26 Sep 2022 21:43:29 - > > @@ -372,7 +372,7 @@ engine(int debug, int verbose) > > if (unveil(NULL, NULL) == -1) > > fatal("unveil"); > > > > - setproctitle("%s", "engine"); > > + setproctitle("engine"); > > log_procinit("engine"); > > > > if (setgroups(1, &pw->pw_gid) || > > Index: sbin/slaacd/frontend.c > > === > > RCS file: /cvs/src/sbin/slaacd/frontend.c,v > > retrieving revision 1.64 > > diff -u -p -r1.64 frontend.c > > --- sbin/slaacd/frontend.c 12 Jul 2022 16:54:59 - 1.64 > > +++ sbin/slaacd/frontend.c 26 Sep 2022 21:43:29 - > > @@ -153,7 +153,7 @@ frontend(int debug, int verbose) > > if (unveil(NULL, NULL) == -1) > > fatal("unveil"); > > > > - setproctitle("%s", "frontend"); > > + setproctitle("frontend"); > > log_procinit("frontend"); > > > > if ((ioctlsock = socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0)) == -1) > > Index: sbin/unwind/frontend.c > > === > > RCS file: /cvs/src/sbin/unwind/frontend.c,v > > retrieving revision 1.73 > > diff -u -p -r1.73 frontend.c > > --- sbin/unwind/frontend.c 13 Mar 2022 15:14:01 - 1.73 > > +++ sbin/unwind/frontend.c 26 Sep 2022 21:43:30 - > > @@ -207,7 +207,7 @@ frontend(int debug, int verbose) > > if (chdir("/") == -1) > > fatal("chdir(\"/\")"); > > > > - setproctitle("%s", "frontend"); > > + setproctitle("frontend"); > > log_procinit("frontend"); > > > > if (setgroups(1, &pw->pw_gid) || > > Index: sbin/unwind/resolver.c > > === > > RCS file: /cvs/src/sbin/unwind/resolver.c,v > > retrieving revision 1.155 > > diff -u -p -r1.155 resolver.c > > --- sbin/unwind/resolver.c 12 Mar 2022 14:35:29 - 1.155 > > +++ sbin/unwind/resolver.c 26 Sep 2022 21:43:30 - > > @@ -368,7 +368,7 @@ resolver(int debug, int verbose) > > if ((pw = getpwnam(UNWIND_USER)) == NULL) > > fatal("getpwnam"); > > > > - setproctitle("%s", "resolver"); > > + setproctitle("resolver"); > > log_procinit("resolver"); > > > > if (setgroups(1, &pw->pw_gid) || > > Index: usr.bin/ssh/sshd.c > > === > > RCS file: /cvs/src/usr.bin/ssh/sshd.c,v > > retrieving revision 1.591 > > diff -u -p -r1.591 sshd.c > > --- usr.bin/ssh/sshd.c 17 Sep 2022 10:34:29 - 1.591 > > +++ usr.bin/ssh/sshd.c 26 Sep 2022 21:43:34 - > > @@ -492,7 +492,7 @@ privsep_preauth(struct ssh *ssh) > > set_log_handler(mm_log_handler, pmonitor); > > > > privsep_preauth_child(); > > - setproctitle("%s", "[net]"); > > + setproctitle("[net]"); > > if (box != NULL) > > ssh_sandbox_child(box); > > > > @@ -1627,7 +
snmpd: allow NULL in appl_varbind_valid
varbind was designed to allow both a ber NULL and a NULL pointer for value. The ber NULL case is there for when it was received via a PDU. The NULL pointer case can happen if application.c runs into a timeout or when a backend runs into problems. The NULL pointer case however was overlooked in appl_varbind_valid and results in an "missing value" error, (needlessly) terminating the connection to the backend. Found the hard way by Mischa Peters while stress testing agentx support for vmd. OK? martijn@ Index: application.c === RCS file: /cvs/src/usr.sbin/snmpd/application.c,v retrieving revision 1.15 diff -u -p -r1.15 application.c --- application.c 31 Aug 2022 09:19:22 - 1.15 +++ application.c 13 Sep 2022 09:59:19 - @@ -1170,8 +1170,11 @@ appl_varbind_valid(struct appl_varbind * int eomv = 0; if (varbind->av_value == NULL) { - *errstr = "missing value"; - return 0; + if (!null) { + *errstr = "missing value"; + return 0; + } + return 1; } if (varbind->av_value->be_class == BER_CLASS_UNIVERSAL) { switch (varbind->av_value->be_type) {
libagentx: NULL deref on varbinds after connection reset
When a connection is reset while we still have an outstanding request, the connection from the request to the rest of the structure is removed, so we don't send any old data over the new connection. However, the current code dereferences axc at a couple of places before we check it for NULL. Found the hard way by Mischa Peters while stress testing agentx support for vmd. OK? martijn@ Index: agentx.c === RCS file: /cvs/src/lib/libagentx/agentx.c,v retrieving revision 1.16 diff -u -p -r1.16 agentx.c --- agentx.c29 Aug 2022 12:17:24 - 1.16 +++ agentx.c13 Sep 2022 09:51:02 - @@ -2575,8 +2575,8 @@ static void agentx_get_finalize(struct agentx_get *axg) { struct agentx_context *axc = axg->axg_axc; - struct agentx_session *axs = axc->axc_axs; - struct agentx *ax = axs->axs_ax; + struct agentx_session *axs; + struct agentx *ax; size_t i, j, nvarbind = 0; uint16_t error = 0, index = 0; struct ax_varbind *vbl; @@ -2591,11 +2591,14 @@ agentx_get_finalize(struct agentx_get *a } } - if (axg->axg_axc == NULL) { + if (axc == NULL) { agentx_get_free(axg); return; } + axs = axc->axc_axs; + ax = axs->axs_ax; + if ((vbl = calloc(nvarbind, sizeof(*vbl))) == NULL) { agentx_log_axg_warn(axg, "Couldn't parse request"); agentx_get_free(axg); @@ -2655,12 +2658,14 @@ agentx_get_free(struct agentx_get *axg) { struct agentx_varbind *axv; struct agentx_object *axo; - struct agentx *ax = axg->axg_axc->axc_axs->axs_ax; + struct agentx *ax; struct agentx_varbind_index *index; size_t i, j; - if (axg->axg_axc != NULL) + if (axg->axg_axc != NULL) { + ax = axg->axg_axc->axc_axs->axs_ax; TAILQ_REMOVE(&(ax->ax_getreqs), axg, axg_ax_getreqs); + } for (i = 0; i < axg->axg_nvarbind; i++) { axv = &(axg->axg_varbind[i]); @@ -2701,6 +2706,11 @@ agentx_varbind_start(struct agentx_varbi agentx_log_axg_fatalx(axv->axv_axg, "%s: axv_initialized not set", __func__); #endif + + if (axc == NULL) { + agentx_varbind_error_type(axv, AX_PDU_ERROR_PROCESSINGERROR, 1); + return; + } bcopy(&(axv->axv_vb.avb_oid), &(axo_search.axo_oid), sizeof(axo_search.axo_oid));
vmd: Add support for agentx
{ "agentx", AGENTX }, { "allow", ALLOW }, { "boot", BOOT }, { "cdrom", CDROM }, + { "context",CONTEXT}, { "delay", DELAY }, { "device", DEVICE }, { "disable",DISABLE }, @@ -793,6 +838,7 @@ lookup(char *s) { "net",NET }, { "owner", OWNER }, { "parallel", PARALLEL }, + { "path", PATH }, { "prefix", PREFIX }, { "rdomain",RDOMAIN }, { "size", SIZE }, @@ -1148,6 +1194,10 @@ parse_config(const char *filename) /* Set the default switch type */ (void)strlcpy(vsw_type, VMD_SWITCH_TYPE, sizeof(vsw_type)); + + env->vmd_cfg.cfg_agentx.ax_enabled = 0; + env->vmd_cfg.cfg_agentx.ax_context[0] = '\0'; + env->vmd_cfg.cfg_agentx.ax_path[0] = '\0'; yyparse(); errors = file->errors; Index: proc.h === RCS file: /cvs/src/usr.sbin/vmd/proc.h,v retrieving revision 1.20 diff -u -p -r1.20 proc.h --- proc.h 16 Jun 2021 16:55:02 - 1.20 +++ proc.h 3 Sep 2022 11:06:31 - @@ -79,6 +79,7 @@ TAILQ_HEAD(ctl_connlist, ctl_conn); enum privsep_procid { PROC_PARENT = 0, PROC_CONTROL, + PROC_AGENTX, PROC_VMM, PROC_PRIV, PROC_MAX, Index: vm.conf.5 === RCS file: /cvs/src/usr.sbin/vmd/vm.conf.5,v retrieving revision 1.58 diff -u -p -r1.58 vm.conf.5 --- vm.conf.5 4 Aug 2022 11:50:51 - 1.58 +++ vm.conf.5 3 Sep 2022 11:06:31 - @@ -91,6 +91,19 @@ vm "vm1.example.com" { .Sh GLOBAL CONFIGURATION The following setting can be configured globally: .Bl -tag -width Ds +.It Ic agentx Oo Ic context Ar context Oc Oo Ic path Ar path Oc +Export vm metrics via an AgentX compatible +.Pq snmp +daemon by connecting to +.Ar path . +Metrics can be found under the vmMIB subtree +.Pq mib-2.236 . +If +.Ar path +is omitted it will default to +.Pa /var/agentx/master . +.Ar Context +is the SNMPv3 context and can usually be omitted. .It Ic local prefix Ar address Ns Li / Ns Ar prefix Set the network prefix that is used to allocate subnets for local interfaces, see Index: vm_agentx.c === RCS file: vm_agentx.c diff -N vm_agentx.c --- /dev/null 1 Jan 1970 00:00:00 - +++ vm_agentx.c 3 Sep 2022 11:06:31 - @@ -0,0 +1,537 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2022 Martijn van Duren + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "proc.h" +#include "vmd.h" + +struct conn { + struct event ev; + struct agentx *agentx; +}; + +void vm_agentx_run(struct privsep *, struct privsep_proc *, void *); +int vm_agentx_dispatch_parent(int, struct privsep_proc *, struct imsg *); +void vm_agentx_configure(struct vmd_agentx *); +static void vm_agentx_nofd(struct agentx *, void *, int); +static void vm_agentx_tryconnect(int, short, void *); +static void vm_agentx_read(int, short, void *); +static void vm_agentx_flush_pending(void); +static int vm_agentx_sortvir(const void *, const void *); +static int vm_agentx_adminstate(int); +static int vm_agentx_operstate(int); +static void vm_agentx_vmHvSoftware(struct agentx_varbind *); +static void vm_agentx_vmHvVersion(struct agentx_varbind *); +static void vm_agentx_vmHvObjectID(struct agentx_varbind *); +static void vm_agentx_vminfo(struct agentx_varbind *); + +static struct agentx_index *vmIndex; +static struct agentx_object *vmNumber, *vmName, *vmUUID, *vmOSType; +static struct agentx_object *vmAdminState, *vmOperState, *vmAutoStart; +static str
snmpd(8): Fix searchrange end length in agentx
I think this one speaks for itself. OK? martijn@ ? obj Index: application_agentx.c === RCS file: /cvs/src/usr.sbin/snmpd/application_agentx.c,v retrieving revision 1.2 diff -u -p -r1.2 application_agentx.c --- application_agentx.c29 Aug 2022 18:10:48 - 1.2 +++ application_agentx.c30 Aug 2022 14:49:52 - @@ -660,7 +660,7 @@ appl_agentx_getnext(struct appl_backend srl[i].asr_start.aoi_id[j] = vb->av_oid.bo_id[j]; srl[i].asr_stop.aoi_include = 0; srl[i].asr_stop.aoi_idlen = vb->av_oid_end.bo_n; - for (j = 0; j < vb->av_oid.bo_n; j++) + for (j = 0; j < vb->av_oid_end.bo_n; j++) srl[i].asr_stop.aoi_id[j] = vb->av_oid_end.bo_id[j]; } if ((context = appl_agentx_string2ostring(ctx, &string)) == NULL) {
snmpd(8): Allow registering above subtree allocated region
So right now we disallow allocation of a region if a sub-region has the subtree flag set. This logic is inverted compared to it's intention, which is that if you allocate a region with subtree it should check that the entire region below it is available (application.c:242 should say "subtree" instead of "region->subtree"). However, since the subtree flag is intended for backends that are normally only initialized on startup and we now have blocklist, chances for overlap increase and could possible disable bigger regions than intended. I think it's safe to remove this entire overlap check because of the following reasoning: - First the blocklist is loaded. This should prevent allocating anything underneath it and expects things to be loaded on top of it - legacy (internal): This could have regions allocated over it and should have blocklist regions underneath it - agentx (as native backend): Same as legacy - agentx (dynamic applications): Only get their chance when everything already has been initialised and thus no risk of being inserted below a "subtree" region, because that's being captured by the first overlap check. OK? martijn@ Index: application.c === RCS file: /cvs/src/usr.sbin/snmpd/application.c,v retrieving revision 1.12 diff -u -p -r1.12 application.c --- application.c 29 Aug 2022 18:05:08 - 1.12 +++ application.c 30 Aug 2022 14:47:23 - @@ -237,14 +237,6 @@ appl_region(struct appl_context *ctx, ui region->ar_backend != backend) goto overlap; - search.ar_oid = *oid; - region = RB_NFIND(appl_regions, &(ctx->ac_regions), &search); - if (region != NULL && region->ar_subtree && - region->ar_backend != backend && ( - appl_region_cmp(&search, region) == 0 || - appl_region_cmp(&search, region) == -2)) - goto overlap; - if ((nregion = malloc(sizeof(*nregion))) == NULL) { log_warn("%s: Can't register %s: Processing error", backend->ab_name, oidbuf);
snmpd(8): Better determining searchrange end
Doing overlapping regions is hard... At application.c:1341 we currently assign region->ar_oid to oid. However, with overlapping regions this can cause a recursion, because region might be the parent of the previous region that would cause the oid to traverse back and cause a loop. This can easily be fixed by changing this line to vb->av_oid, but I also found that the code doesn't produce the cleanest searchrange end, adjacent regions might not be taken into account on a single run because of the else statement on line 1363. With the following registration it will result in: blocklist: Registering 1.3.6.1.4.1.30155.1.9.129 context() priority(1) timeout(1.50s) AgentX(556403008/718113859): Registering 1.3.6.1.4.1.30155.1 context() priority(1) timeout(5.00s) AgentX(556403008/718113859): Registering 1.3.6.1.4.1.30155.2 context() priority(1) timeout(5.00s) AgentX(556403008/718113859): Registering 1.3.6.1.4.1.30155.5 context() priority(1) timeout(5.00s) AgentX(556403008/718113859): Registering 1.3.6.1.4.1.30155.6 context() priority(1) timeout(5.00s) ... GetNextRequest{333882979, 0, 0, {{1.3.6.1.4.1.30155.1.9.128.1.24.3:null}}} AgentX(556403008/718113859): GetNextRequest{1517163039, 0, 0, {{1.3.6.1.4.1.30155.1.9.128.1.24.3-1.3.6.1.4.1.30155.1.9.129:null}}} AgentX(556403008/718113859): Response{1517163039, 0, 0, {{1.3.6.1.4.1.30155.1.9.128.1.24.3:endOfMibView}}} blocklist: GetNextRequest{1339241491, 0, 0, {{1.3.6.1.4.1.30155.1.9.129(incl)-1.3.6.1.4.1.30155.1.9.130:null}}} blocklist: Response{1339241491, 0, 0, {{1.3.6.1.4.1.30155.1.9.129:endOfMibView}}} AgentX(556403008/718113859): GetNextRequest{1545296030, 0, 0, {{1.3.6.1.4.1.30155.1.9.130(incl)-1.3.6.1.4.1.30155.2:null}}} AgentX(556403008/718113859): Response{1545296030, 0, 0, {{1.3.6.1.4.1.30155.1.10.1.0:2}}} to ... GetNextRequest{2078129483, 0, 0, {{1.3.6.1.4.1.30155.1.9.129.1.1.1.192.168.153.0.24:null}}} blocklist: GetNextRequest{-1870096078, 0, 0, {{1.3.6.1.4.1.30155.1.9.129.1.1.1.192.168.153.0.24-1.3.6.1.4.1.30155.1.9.130:null}}} blocklist: Response{-1870096078, 0, 0, {{1.3.6.1.4.1.30155.1.9.129.1.1.1.192.168.153.0.24:endOfMibView}}} AgentX(3175611302/2074212055): GetNextRequest{-867062125, 0, 0, {{1.3.6.1.4.1.30155.1.9.130(incl)-1.3.6.1.4.1.30155.3:null}}} AgentX(3175611302/2074212055): Response{-867062125, 0, 0, {{1.3.6.1.4.1.30155.1.10.1.0:2}}} OK? martijn@ Index: application.c === RCS file: /cvs/src/usr.sbin/snmpd/application.c,v retrieving revision 1.12 diff -u -p -r1.12 application.c --- application.c 29 Aug 2022 18:05:08 - 1.12 +++ application.c 30 Aug 2022 14:29:50 - @@ -1287,7 +1287,7 @@ appl_varbind_backend(struct appl_varbind struct appl_request_upstream *ureq = ivb->avi_request_upstream; struct appl_region search, *region, *pregion; struct appl_varbind *vb = &(ivb->avi_varbind); - struct ber_oid oid; + struct ber_oid oid, nextsibling; int next, cmp; next = ureq->aru_pdu->be_type == SNMP_C_GETNEXTREQ || @@ -1338,33 +1338,41 @@ appl_varbind_backend(struct appl_varbind } ivb->avi_region = region; if (next) { - oid = region->ar_oid; + oid = vb->av_oid; + /* +* For the searchrange end we only want contiguous regions. +* This means directly connecting, or overlapping with the same +* backend. +*/ do { pregion = region; region = appl_region_next(ureq->aru_ctx, &oid, pregion); - if (region != NULL && - appl_region_cmp(region, pregion) > 0) - oid = region->ar_oid; - else + if (region == NULL) { + oid = pregion->ar_oid; ober_oid_nextsibling(&oid); - } while (region != NULL && - region->ar_backend == pregion->ar_backend); - if (region == NULL) { - vb->av_oid_end = pregion->ar_oid; - if (pregion->ar_instance && - vb->av_oid_end.bo_n < BER_MAX_OID_LEN) - vb->av_oid_end.bo_id[vb->av_oid_end.bo_n++] = 0; - else - ober_oid_nextsibling(&(vb->av_oid_end)); - } else { - if (ober_oid_cmp(&(region->ar_oid), - &(ivb->avi_region->ar_oid)) == 2) - vb->av_oid_end = region->ar_oid; - else { - vb->av_oid_end = ivb->avi_region->ar_oid; - ober_oid_nextsibling(&(vb->av_oid_end)); + break; } - } +
snmpd(8): Minor logging cleanup
Apparently I mistyped one AgentX as Agentx, and when moving sess_id to uint32_t (in an early draft) I forgot to adjust the %d in two places. OK? martijn@ Index: application_agentx.c === RCS file: /cvs/src/usr.sbin/snmpd/application_agentx.c,v retrieving revision 1.1 diff -u -p -r1.1 application_agentx.c --- application_agentx.c23 Aug 2022 08:56:20 - 1.1 +++ application_agentx.c29 Aug 2022 17:50:50 - @@ -145,7 +145,7 @@ appl_agentx_listen(struct agentx_master bind(master->axm_fd, (struct sockaddr *)&(master->axm_sun), sizeof(master->axm_sun)) == -1 || listen(master->axm_fd, 5)) { - log_warn("Agentx: listen %s", master->axm_sun.sun_path); + log_warn("AgentX: listen %s", master->axm_sun.sun_path); umask(mask); return; } @@ -208,7 +208,7 @@ appl_agentx_accept(int masterfd, short e appl_agentx_recv, conn); event_add(&(conn->conn_rev), NULL); event_set(&(conn->conn_wev), fd, EV_WRITE, appl_agentx_send, conn); - log_info("AgentX(%d): new connection", conn->conn_id); + log_info("AgentX(%"PRIu32"): new connection", conn->conn_id); return; fail: @@ -442,7 +442,7 @@ appl_agentx_open(struct appl_agentx_conn if (asprintf(&(session->sess_backend.ab_name), "AgentX(%"PRIu32"/%"PRIu32")", conn->conn_id, session->sess_id) == -1) { - log_warn("AgentX(%d): asprintf: Open Failed", + log_warn("AgentX(%"PRIu32"): asprintf: Open Failed", conn->conn_id); goto fail; }
snmpd(8): Allow overlapping region from same backend
Right now we don't allow overlapping regions when the subtree flag is set, . However I don't see a reason why a single backend can't make an overlapping region with itself. I would also like to use this feature when moving mib.c code into an libagentx based backend. OK? martijn@ Index: application.c === RCS file: /cvs/src/usr.sbin/snmpd/application.c,v retrieving revision 1.10 diff -u -p -r1.10 application.c --- application.c 29 Aug 2022 13:25:18 - 1.10 +++ application.c 29 Aug 2022 17:39:15 - @@ -223,12 +223,14 @@ appl_region(struct appl_context *ctx, ui * This allows us to keep control of certain regions like system. */ region = appl_region_find(ctx, oid); - if (region != NULL && region->ar_subtree) + if (region != NULL && region->ar_subtree && + region->ar_backend != backend) goto overlap; search.ar_oid = *oid; region = RB_NFIND(appl_regions, &(ctx->ac_regions), &search); - if (region != NULL && region->ar_subtree && ( + if (region != NULL && region->ar_subtree && + region->ar_backend != backend && ( appl_region_cmp(&search, region) == 0 || appl_region_cmp(&search, region) == -2)) goto overlap;
snmpd(8): make sure oidbuf is properly initialized on overlapping regions
I think the subject speaks for itself. Not a really big problem, since non of the available software that we currently have in base/ports have overlapping regions, but definitely worth fixing. OK? martijn@ Index: application.c === RCS file: /cvs/src/usr.sbin/snmpd/application.c,v retrieving revision 1.10 diff -u -p -r1.10 application.c --- application.c 29 Aug 2022 13:25:18 - 1.10 +++ application.c 29 Aug 2022 17:30:44 - @@ -218,6 +218,16 @@ appl_region(struct appl_context *ctx, ui char oidbuf[1024], regionbuf[1024], subidbuf[11]; size_t i; + /* Don't use smi_oid2string, because appl_register can't use it */ + oidbuf[0] = '\0'; + for (i = 0; i < oid->bo_n; i++) { + if (i != 0) + strlcat(oidbuf, ".", sizeof(oidbuf)); + snprintf(subidbuf, sizeof(subidbuf), "%"PRIu32, + oid->bo_id[i]); + strlcat(oidbuf, subidbuf, sizeof(oidbuf)); + } + /* * Don't allow overlap when subtree flag is set. * This allows us to keep control of certain regions like system. @@ -233,15 +243,6 @@ appl_region(struct appl_context *ctx, ui appl_region_cmp(&search, region) == -2)) goto overlap; - /* Don't use smi_oid2string, because appl_register can't use it */ - oidbuf[0] = '\0'; - for (i = 0; i < oid->bo_n; i++) { - if (i != 0) - strlcat(oidbuf, ".", sizeof(oidbuf)); - snprintf(subidbuf, sizeof(subidbuf), "%"PRIu32, - oid->bo_id[i]); - strlcat(oidbuf, subidbuf, sizeof(oidbuf)); - } if ((nregion = malloc(sizeof(*nregion))) == NULL) { log_warn("%s: Can't register %s: Processing error", backend->ab_name, oidbuf);
snmpd(8): don't traverse back in tree
When we have 2 overlapping regions within the same backend the current code takes the OID of the parent region after the child region returned an EOMV. This is of course wrong and creates an infinite loop. OK? martijn@ Index: application.c === RCS file: /cvs/src/usr.sbin/snmpd/application.c,v retrieving revision 1.6 diff -u -p -r1.6 application.c --- application.c 30 Jun 2022 11:28:36 - 1.6 +++ application.c 22 Jul 2022 15:07:53 - @@ -1260,6 +1260,7 @@ appl_varbind_backend(struct appl_varbind struct appl_request_upstream *ureq = ivb->avi_request_upstream; struct appl_region search, *region, *pregion; struct appl_varbind *vb = &(ivb->avi_varbind); + struct ber_oid oid; int next, cmp; next = ureq->aru_pdu->be_type == SNMP_C_GETNEXTREQ || @@ -1310,10 +1311,15 @@ appl_varbind_backend(struct appl_varbind } ivb->avi_region = region; if (next) { + oid = region->ar_oid; do { pregion = region; - region = appl_region_next(ureq->aru_ctx, - &(region->ar_oid), pregion); + region = appl_region_next(ureq->aru_ctx, &oid, pregion); + if (region != NULL && + appl_region_cmp(region, pregion) > 0) + oid = region->ar_oid; + else + ober_oid_nextsibling(&oid); } while (region != NULL && region->ar_backend == pregion->ar_backend); if (region == NULL) {
snmpd(8): restart requests where backend disappeared.
appl_request_downstream_free gets called from 3 locations: - appl_request_upstream_free: called from appl_request_upstream_reply and cleans up after a request has been answered, whether all the downstream requests have been completed or not (when timed out) - appl_response: When a downstream reqeuest has been completed - appl_close: When a downstream backend has been closed. appl_request_upstream_free is already done with the upstream request and appl_response already automatically starts the next iteration of missing varbinds. The problem arrises when appl_close is called (e.g. a backend disappears unexpected) while there are still pending downstream requests. In that case all references to the upstream request are lost and we have a memory leak. Diff below resets the pending varbinds to new and restarts the parsing procedure. OK? martijn@ Index: application.c === RCS file: /cvs/src/usr.sbin/snmpd/application.c,v retrieving revision 1.6 diff -u -p -r1.6 application.c --- application.c 30 Jun 2022 11:28:36 - 1.6 +++ application.c 22 Jul 2022 14:59:00 - @@ -716,6 +716,7 @@ void appl_request_downstream_free(struct appl_request_downstream *dreq) { struct appl_varbind_internal *vb; + int retry = 0; if (dreq == NULL) return; @@ -723,9 +724,16 @@ appl_request_downstream_free(struct appl RB_REMOVE(appl_requests, &(dreq->ard_backend->ab_requests), dreq); evtimer_del(&(dreq->ard_timer)); - for (vb = dreq->ard_vblist; vb != NULL; vb = vb->avi_next) + for (vb = dreq->ard_vblist; vb != NULL; vb = vb->avi_next) { vb->avi_request_downstream = NULL; + if (vb->avi_state == APPL_VBSTATE_PENDING) { + vb->avi_state = APPL_VBSTATE_NEW; + retry = 1; + } + } + if (retry) + appl_request_upstream_resolve(dreq->ard_request); free(dreq); }
snmpd(8): honour searchrange end
This is the snmpd(8) part of searchrange end issue mentioned in my libagentx diff from yesterday.[0] Since searchranges are an agentx specific thing I implemented two cases: 1) Backends that support searchranges set ab_range to 1 (application_agentx.c) and check the av_oid_end in appl_varbind_valid. If it fails here we generate a GENERR and disconnect the backend. 2) Backends that don't support searchranges set ab_range to 0 and are not checked for overflow in appl_varbind_valid, but instead treat the response as ENDOFMIBVIEW and continue searching at av_oid_end. Since this is already the 3rd EOMV case added I decided to move to a simple variable and do the reset-logic in a single place. While here, also fix an indent in appl_close. OK? martijn@ [0] https://marc.info/?l=openbsd-tech&m=165843466721371&w=2 Index: application.c === RCS file: /cvs/src/usr.sbin/snmpd/application.c,v retrieving revision 1.6 diff -u -p -r1.6 application.c --- application.c 30 Jun 2022 11:28:36 - 1.6 +++ application.c 22 Jul 2022 13:13:43 - @@ -126,7 +126,7 @@ void appl_request_downstream_send(struct void appl_request_downstream_timeout(int, short, void *); void appl_request_upstream_reply(struct appl_request_upstream *); int appl_varbind_valid(struct appl_varbind *, struct appl_varbind *, int, int, -const char **); +int, const char **); int appl_varbind_backend(struct appl_varbind_internal *); void appl_varbind_error(struct appl_varbind_internal *, enum appl_error); void appl_report(struct snmp_message *, int32_t, struct ber_oid *, @@ -541,7 +541,7 @@ appl_close(struct appl_backend *backend) RB_FOREACH_SAFE(request, appl_requests, &(backend->ab_requests), trequest) - appl_request_downstream_free(request); + appl_request_downstream_free(request); } struct appl_region * @@ -1025,14 +1025,13 @@ appl_response(struct appl_backend *backe { struct appl_request_downstream *dreq, search; struct appl_request_upstream *ureq = NULL; - struct appl_region *nregion; const char *errstr; char oidbuf[1024]; enum snmp_pdutype pdutype; struct appl_varbind *vb; struct appl_varbind_internal *origvb = NULL; int invalid = 0; - int next = 0; + int next = 0, eomv; int32_t i; appl_pdu_log(backend, SNMP_C_RESPONSE, requestid, error, index, vblist); @@ -1055,7 +1054,7 @@ appl_response(struct appl_backend *backe for (i = 1; vb != NULL; vb = vb->av_next, i++) { if (!appl_varbind_valid(vb, origvb == NULL ? NULL : &(origvb->avi_varbind), next, -error != APPL_ERROR_NOERROR, &errstr)) { +error != APPL_ERROR_NOERROR, backend->ab_range, &errstr)) { smi_oid2string(&(vb->av_oid), oidbuf, sizeof(oidbuf), 0); log_warnx("%s: %"PRIu32" %s: %s", @@ -1068,35 +1067,36 @@ appl_response(struct appl_backend *backe appl_varbind_error(origvb, error); origvb->avi_state = APPL_VBSTATE_DONE; origvb->avi_varbind.av_oid = vb->av_oid; - if (vb->av_value != NULL && + + eomv = vb->av_value != NULL && vb->av_value->be_class == BER_CLASS_CONTEXT && - vb->av_value->be_type == APPL_EXC_ENDOFMIBVIEW) { - nregion = appl_region_next(ureq->aru_ctx, - &(vb->av_oid), origvb->avi_region); - if (nregion != NULL) { - ober_free_elements(vb->av_value); - origvb->avi_varbind.av_oid = - nregion->ar_oid; - origvb->avi_varbind.av_include = 1; - vb->av_value = NULL; - origvb->avi_state = APPL_VBSTATE_NEW; - } - } + vb->av_value->be_type == APPL_EXC_ENDOFMIBVIEW; + /* +* Treat results past av_oid_end for backends that +* don't support searchranges as EOMV +*/ + eomv |= !backend->ab_range && next && + ober_oid_cmp(&(vb->av_oid), + &(origvb->avi_varbind.av_oid_end)) > 0; /* RFC 3584 section 4.2.2.1 */ if (ureq->aru_pduversion == SNMP_V1 && vb->av_value != NULL && vb->av_value->be_class == BER_CLASS_APPLICATION && vb->
libagentx: honour searchrange end
When doing a getnext/getbulk request, agentx diverges from snmp that it sends a searchrange, instead of a simple next. The end oid is currently not correctly handled by both snmpd(8) and libagentx. If a backend has two ranges with one or more other backends having a region claimed in between snmpd(8) can skip over that other region. To make it concrete. Moving most of mib.c into an libagentx based application make it claim ipfRouteEntStatus and dot1dBaseNumPorts. But the snmp tree (which resides in between) remains part of snmpd(8). Doing a getnext on ipfRouteEntStatus.9 would make snmpd(8) skip to dot1dBaseNumPorts.0, instead of snmpInPkts.0. This diff should fix the libagentx side of things. OK? martijn@ Index: agentx.c === RCS file: /cvs/src/lib/libagentx/agentx.c,v retrieving revision 1.15 diff -u -p -r1.15 agentx.c --- agentx.c19 Jul 2022 19:25:42 - 1.15 +++ agentx.c21 Jul 2022 20:14:04 - @@ -2722,7 +2722,8 @@ agentx_varbind_start(struct agentx_varbi getnext: while (axo != NULL && axo->axo_cstate != AX_CSTATE_OPEN) axo = RB_NEXT(axc_objects, &(axc->axc_objects), axo); - if (axo == NULL) { + if (axo == NULL || + ax_oid_cmp(&(axo->axo_oid), &(axv->axv_end)) > 0) { agentx_varbind_endofmibview(axv); return; }
libagentx: enable objects on dynamic index enabling
Diff below is needed for migrating snmpd's mib.c to a libagentx based environment. Specifically the OPENBSD-MEM-MIB:memIfTable, which relies on IF-MIB:ifIndex. The issue bubbles forth from the following behaviour: In libagentx both an object and an index are enabled (started) based when their respective region is started. The order in which these regions are started is undetermined. For agentx_object the agentx_object_start function is halted when not all indices are in AX_CSTATE_OPEN. The problem arises from the fact that agentx_index_finalize (the function called when we know that the request to be opened can be answered) is combined with a dynamic index (an index not registered at the snmpd server, but determined by the libagentx based application): The index is placed in the AX_CSTATE_OPEN state, but it returns immediately, ignoring any objects that rely on it being opened. The diff below replaces the return statement to a jump statement to the final part of the function trying to open the functions. OK? martijn@ Index: agentx.c === RCS file: /cvs/src/lib/libagentx/agentx.c,v retrieving revision 1.14 diff -u -p -r1.14 agentx.c --- agentx.c24 Oct 2021 18:03:27 - 1.14 +++ agentx.c17 Jul 2022 12:03:55 - @@ -1660,7 +1660,7 @@ agentx_index_finalize(struct ax_pdu *pdu #endif if (axi->axi_type == AXI_TYPE_DYNAMIC) { axi->axi_cstate = AX_CSTATE_OPEN; - return 0; + goto objects_start; } resp = &(pdu->ap_payload.ap_response); @@ -1717,6 +1717,7 @@ agentx_index_finalize(struct ax_pdu *pdu if (axi->axi_dstate == AX_DSTATE_CLOSE) return agentx_index_close(axi); + objects_start: /* TODO Make use of range_subid register */ for (i = 0; i < axi->axi_objectlen; i++) { if (axi->axi_object[i]->axo_dstate == AX_DSTATE_OPEN) {
Re: snmpd(8): clean up variable printing
On Wed, 2022-01-19 at 16:23 +0100, Martijn van Duren wrote: > The new code uses smi_print_element when debugging is enabled to trace > calls. Unfortunately the current smi_print_element lacks in quite a few > departments. This diff rewrites smi_print_element to be more concise > than what we currently have, without moving into the more complex > territory that snmp(1) has. > > Unknown types are printed in a similar fashion to what tcpdump(8)'s > snmp output does. > > This change helps mostly with exceptions (NOSUCH{OBJECT,INSTANCE} and > ENDOFMIBVIEW) and distinguishing between different integer types. > > I kept the current implementation under smi_print_element_legacy, so > that we don't change the output of trap handlers. We should probably > revisit that one at some point, but I don't think to go into that > territory right now. > > OK? > > martijn@ > > p.s. I'm not particularly thrilled about the type hinting, but it's > the cleanest that I could come up with without being too much of an > eyesore or filling the screen up even further. > ping Index: smi.c === RCS file: /cvs/src/usr.sbin/snmpd/smi.c,v retrieving revision 1.30 diff -u -p -r1.30 smi.c --- smi.c 21 Oct 2021 15:08:15 - 1.30 +++ smi.c 29 Jun 2022 11:44:26 - @@ -46,6 +46,7 @@ #include "snmpd.h" #include "mib.h" +#include "application.h" #define MINIMUM(a, b) (((a) < (b)) ? (a) : (b)) @@ -461,8 +462,9 @@ smi_debug_elements(struct ber_element *r } #endif +/* Keep around so trap handle scripts don't break */ char * -smi_print_element(struct ber_element *root) +smi_print_element_legacy(struct ber_element *root) { char*str = NULL, *buf, *p; size_t len, i; @@ -520,6 +522,140 @@ smi_print_element(struct ber_element *ro case BER_TYPE_SET: default: str = strdup(""); + break; + } + + return (str); + + fail: + free(str); + return (NULL); +} + +char * +smi_print_element(struct ber_element *root) +{ + char*str = NULL, *buf, *p; + long longv; + struct ber_oid o; + char strbuf[BUFSIZ]; + + switch (root->be_class) { + case BER_CLASS_UNIVERSAL: + switch (root->be_type) { + case BER_TYPE_INTEGER: + if (ober_get_integer(root, &v) == -1) + goto fail; + if (asprintf(&str, "%lld", v) == -1) + goto fail; + break; + case BER_TYPE_OBJECT: + if (ober_get_oid(root, &o) == -1) + goto fail; + if (asprintf(&str, "%s", smi_oid2string(&o, strbuf, + sizeof(strbuf), 0)) == -1) + goto fail; + break; + case BER_TYPE_OCTETSTRING: + if (ober_get_string(root, &buf) == -1) + goto fail; + p = reallocarray(NULL, 4, root->be_len + 1); + if (p == NULL) + goto fail; + strvisx(p, buf, root->be_len, VIS_NL); + if (asprintf(&str, "\"%s\"", p) == -1) { + free(p); + goto fail; + } + free(p); + break; + case BER_TYPE_NULL: + if (asprintf(&str, "null") == -1) + goto fail; + break; + default: + /* Should not happen in a valid SNMP packet */ + if (asprintf(&str, "[U/%u]", root->be_type) == -1) + goto fail; + break; + } + break; + case BER_CLASS_APPLICATION: + switch (root->be_type) { + case SNMP_T_IPADDR: + if (ober_get_string(root, &buf) == -1) + goto fail; + if (asprintf(&str, "%s", + inet_ntoa(*(struct in_addr *)buf)) == -1) + goto fail; + break; + case SNMP_T_COUNTER32: + if (ober_get_integer(root, &v) == -1) + goto fail; + if (asprintf(&str, "%lld(c32)", v) == -1) +
Re: snmpd(8): Add blocklist feature
On Tue, 2022-06-28 at 12:33 +0200, Martijn van Duren wrote: > On Tue, 2022-06-28 at 12:21 +0200, Martijn van Duren wrote: > > On Tue, 2022-06-28 at 10:21 +0200, Martijn van Duren wrote: > > > Back in 2020 florian@ added the filter-pf-addresses keyword. > > > Although useful, I always felt it was a bit too case-specific. The diff > > > below adds a new blocklist feature/backend, which takes hold of an > > > entire subtree, effectively removing it from the tree. > > > > > > With this I've deprecated the filter-pf-address case and should > > > probably be removed somewhere after 7.4. The filter-routes case can't > > > be removed unfortunately, since its behaviour is not identical, and > > > instead adds filters to the routing socket, preventing updates being > > > pushed to snmpd(8). > > > > > > Feedback/OK? > > > > > > martijn@ > > > > > Also clean up after ourselves if appl_exception fails. > > > *sigh* Missed a return. > And also some cleanup during shutdown. Index: Makefile === RCS file: /cvs/src/usr.sbin/snmpd/Makefile,v retrieving revision 1.18 diff -u -p -r1.18 Makefile --- Makefile19 Jan 2022 11:00:56 - 1.18 +++ Makefile29 Jun 2022 11:35:23 - @@ -3,6 +3,7 @@ PROG= snmpd MAN= snmpd.8 snmpd.conf.5 SRCS= parse.y log.c snmpe.c application.c application_legacy.c \ + application_blocklist.c \ mps.c trap.c mib.c smi.c kroute.c snmpd.c timer.c \ pf.c proc.c usm.c traphandler.c util.c Index: application.c === RCS file: /cvs/src/usr.sbin/snmpd/application.c,v retrieving revision 1.5 diff -u -p -r1.5 application.c --- application.c 27 Jun 2022 10:31:17 - 1.5 +++ application.c 29 Jun 2022 11:35:23 - @@ -148,6 +148,7 @@ RB_PROTOTYPE_STATIC(appl_requests, appl_ void appl_init(void) { + appl_blocklist_init(); appl_legacy_init(); } @@ -156,6 +157,7 @@ appl_shutdown(void) { struct appl_context *ctx, *tctx; + appl_blocklist_shutdown(); appl_legacy_shutdown(); TAILQ_FOREACH_SAFE(ctx, &contexts, ac_entries, tctx) { Index: application.h === RCS file: /cvs/src/usr.sbin/snmpd/application.h,v retrieving revision 1.1 diff -u -p -r1.1 application.h --- application.h 19 Jan 2022 10:59:35 - 1.1 +++ application.h 29 Jun 2022 11:35:23 - @@ -133,3 +133,7 @@ struct ber_element *appl_exception(enum /* application_legacy.c */ voidappl_legacy_init(void); voidappl_legacy_shutdown(void); + +/* application_blocklist.c */ +voidappl_blocklist_init(void); +voidappl_blocklist_shutdown(void); Index: application_blocklist.c === RCS file: application_blocklist.c diff -N application_blocklist.c --- /dev/null 1 Jan 1970 00:00:00 - +++ application_blocklist.c 29 Jun 2022 11:35:23 - @@ -0,0 +1,141 @@ +/* $OpenBSD: application.c,v 1.5 2022/06/27 10:31:17 martijn Exp $ */ + +/* + * Copyright (c) 2022 Martijn van Duren + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include + +#include "application.h" +#include "snmpd.h" + +struct appl_varbind *appl_blocklist_response(size_t); +void appl_blocklist_get(struct appl_backend *, int32_t, int32_t, const char *, +struct appl_varbind *); +void appl_blocklist_getnext(struct appl_backend *, int32_t, int32_t, +const char *, struct appl_varbind *); + +struct appl_backend_functions appl_blocklist_functions = { + .ab_get = appl_blocklist_get, + .ab_getnext = appl_blocklist_getnext, + .ab_getbulk = NULL, +}; + +struct appl_backend appl_blocklist = { + .ab_name = "blocklist", + .ab_cookie = NULL, + .ab_retries = 0, + .ab_fn = &appl_blocklist_functions +}; + +static struct appl_varbind *response = NULL; +static size_t responsesz = 0; +
Re: snmpd(8): Add blocklist feature
On Tue, 2022-06-28 at 12:21 +0200, Martijn van Duren wrote: > On Tue, 2022-06-28 at 10:21 +0200, Martijn van Duren wrote: > > Back in 2020 florian@ added the filter-pf-addresses keyword. > > Although useful, I always felt it was a bit too case-specific. The diff > > below adds a new blocklist feature/backend, which takes hold of an > > entire subtree, effectively removing it from the tree. > > > > With this I've deprecated the filter-pf-address case and should > > probably be removed somewhere after 7.4. The filter-routes case can't > > be removed unfortunately, since its behaviour is not identical, and > > instead adds filters to the routing socket, preventing updates being > > pushed to snmpd(8). > > > > Feedback/OK? > > > > martijn@ > > > Also clean up after ourselves if appl_exception fails. > *sigh* Missed a return. Index: Makefile === RCS file: /cvs/src/usr.sbin/snmpd/Makefile,v retrieving revision 1.18 diff -u -p -r1.18 Makefile --- Makefile19 Jan 2022 11:00:56 - 1.18 +++ Makefile28 Jun 2022 10:32:40 - @@ -3,6 +3,7 @@ PROG= snmpd MAN= snmpd.8 snmpd.conf.5 SRCS= parse.y log.c snmpe.c application.c application_legacy.c \ + application_blocklist.c \ mps.c trap.c mib.c smi.c kroute.c snmpd.c timer.c \ pf.c proc.c usm.c traphandler.c util.c Index: application.c === RCS file: /cvs/src/usr.sbin/snmpd/application.c,v retrieving revision 1.5 diff -u -p -r1.5 application.c --- application.c 27 Jun 2022 10:31:17 - 1.5 +++ application.c 28 Jun 2022 10:32:40 - @@ -148,6 +148,7 @@ RB_PROTOTYPE_STATIC(appl_requests, appl_ void appl_init(void) { + appl_blocklist_init(); appl_legacy_init(); } Index: application.h === RCS file: /cvs/src/usr.sbin/snmpd/application.h,v retrieving revision 1.1 diff -u -p -r1.1 application.h --- application.h 19 Jan 2022 10:59:35 - 1.1 +++ application.h 28 Jun 2022 10:32:40 - @@ -133,3 +133,6 @@ struct ber_element *appl_exception(enum /* application_legacy.c */ voidappl_legacy_init(void); voidappl_legacy_shutdown(void); + +/* application_blocklist.c */ +voidappl_blocklist_init(void); Index: application_blocklist.c === RCS file: application_blocklist.c diff -N application_blocklist.c --- /dev/null 1 Jan 1970 00:00:00 - +++ application_blocklist.c 28 Jun 2022 10:32:40 - @@ -0,0 +1,134 @@ +/* $OpenBSD: application.c,v 1.5 2022/06/27 10:31:17 martijn Exp $ */ + +/* + * Copyright (c) 2022 Martijn van Duren + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include + +#include "application.h" +#include "snmpd.h" + +struct appl_varbind *appl_blocklist_response(size_t); +void appl_blocklist_get(struct appl_backend *, int32_t, int32_t, const char *, +struct appl_varbind *); +void appl_blocklist_getnext(struct appl_backend *, int32_t, int32_t, +const char *, struct appl_varbind *); + +struct appl_backend_functions appl_blocklist_functions = { + .ab_get = appl_blocklist_get, + .ab_getnext = appl_blocklist_getnext, + .ab_getbulk = NULL, +}; + +struct appl_backend appl_blocklist = { + .ab_name = "blocklist", + .ab_cookie = NULL, + .ab_retries = 0, + .ab_fn = &appl_blocklist_functions +}; + +static struct appl_varbind *response = NULL; +static size_t responsesz = 0; + +struct appl_varbind * +appl_blocklist_response(size_t nvarbind) +{ + struct appl_varbind *tmp; + size_t i; + + if (responsesz < nvarbind) { + if ((tmp = recallocarray(response, responsesz, nvarbind, + sizeof(*response))) == NULL) { + log_warn(NULL); + return NULL; + } + responsesz = nvarbind; + response = tmp; + } + for
Re: snmpd(8): Add blocklist feature
On Tue, 2022-06-28 at 10:21 +0200, Martijn van Duren wrote: > Back in 2020 florian@ added the filter-pf-addresses keyword. > Although useful, I always felt it was a bit too case-specific. The diff > below adds a new blocklist feature/backend, which takes hold of an > entire subtree, effectively removing it from the tree. > > With this I've deprecated the filter-pf-address case and should > probably be removed somewhere after 7.4. The filter-routes case can't > be removed unfortunately, since its behaviour is not identical, and > instead adds filters to the routing socket, preventing updates being > pushed to snmpd(8). > > Feedback/OK? > > martijn@ > Also clean up after ourselves if appl_exception fails. Index: Makefile === RCS file: /cvs/src/usr.sbin/snmpd/Makefile,v retrieving revision 1.18 diff -u -p -r1.18 Makefile --- Makefile19 Jan 2022 11:00:56 - 1.18 +++ Makefile28 Jun 2022 10:20:23 - @@ -3,6 +3,7 @@ PROG= snmpd MAN= snmpd.8 snmpd.conf.5 SRCS= parse.y log.c snmpe.c application.c application_legacy.c \ + application_blocklist.c \ mps.c trap.c mib.c smi.c kroute.c snmpd.c timer.c \ pf.c proc.c usm.c traphandler.c util.c Index: application.c === RCS file: /cvs/src/usr.sbin/snmpd/application.c,v retrieving revision 1.5 diff -u -p -r1.5 application.c --- application.c 27 Jun 2022 10:31:17 - 1.5 +++ application.c 28 Jun 2022 10:20:23 - @@ -148,6 +148,7 @@ RB_PROTOTYPE_STATIC(appl_requests, appl_ void appl_init(void) { + appl_blocklist_init(); appl_legacy_init(); } Index: application.h === RCS file: /cvs/src/usr.sbin/snmpd/application.h,v retrieving revision 1.1 diff -u -p -r1.1 application.h --- application.h 19 Jan 2022 10:59:35 - 1.1 +++ application.h 28 Jun 2022 10:20:23 - @@ -133,3 +133,6 @@ struct ber_element *appl_exception(enum /* application_legacy.c */ voidappl_legacy_init(void); voidappl_legacy_shutdown(void); + +/* application_blocklist.c */ +voidappl_blocklist_init(void); Index: application_blocklist.c === RCS file: application_blocklist.c diff -N application_blocklist.c --- /dev/null 1 Jan 1970 00:00:00 - +++ application_blocklist.c 28 Jun 2022 10:20:23 - @@ -0,0 +1,132 @@ +/* $OpenBSD: application.c,v 1.5 2022/06/27 10:31:17 martijn Exp $ */ + +/* + * Copyright (c) 2022 Martijn van Duren + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include + +#include "application.h" +#include "snmpd.h" + +struct appl_varbind *appl_blocklist_response(size_t); +void appl_blocklist_get(struct appl_backend *, int32_t, int32_t, const char *, +struct appl_varbind *); +void appl_blocklist_getnext(struct appl_backend *, int32_t, int32_t, +const char *, struct appl_varbind *); + +struct appl_backend_functions appl_blocklist_functions = { + .ab_get = appl_blocklist_get, + .ab_getnext = appl_blocklist_getnext, + .ab_getbulk = NULL, +}; + +struct appl_backend appl_blocklist = { + .ab_name = "blocklist", + .ab_cookie = NULL, + .ab_retries = 0, + .ab_fn = &appl_blocklist_functions +}; + +static struct appl_varbind *response = NULL; +static size_t responsesz = 0; + +struct appl_varbind * +appl_blocklist_response(size_t nvarbind) +{ + struct appl_varbind *tmp; + size_t i; + + if (responsesz < nvarbind) { + if ((tmp = recallocarray(response, responsesz, nvarbind, + sizeof(*response))) == NULL) { + log_warn(NULL); + return NULL; + } + responsesz = nvarbind; + response = tmp; + } + for (i = 0; i < nvarbind; i++) + response[i].av_next = i + 1 == nvarbind ? + NULL : &(response[i + 1]); + return response; +} + +void
snmpd(8): Allow for symbolic OID names in snmpd.conf
When playing with the blocklist I noticed that the oid directive only supports numeric OIDs. So if florian wants to use filter-pf-table in the future he'd have to set: blocklist 1.3.6.1.4.1.30155.1.9.129 which is pure insanity, if: blocklist pfTblAddrTable could be an option. Diff below changes the "oid" parse.y definition from ober_string2oid to smi_string2oid, which allows for the locally known symbolic names. This also helps out with existing statements ("system oid", "trap handle oid", and "trap receiver", and "oid"). While here, rename sysoid to oid, since it's not used by the system oid anymore and do a little KNF. OK? martijn@ Index: parse.y === RCS file: /cvs/src/usr.sbin/snmpd/parse.y,v retrieving revision 1.73 diff -u -p -r1.73 parse.y --- parse.y 21 Nov 2021 13:33:53 - 1.73 +++ parse.y 28 Jun 2022 09:02:36 - @@ -666,21 +666,20 @@ optwrite : READONLY { $$ = 0; } ; oid: STRING{ - struct ber_oid *sysoid; - if ((sysoid = - calloc(1, sizeof(*sysoid))) == NULL) { + struct ber_oid *oid; + if ((oid = calloc(1, sizeof(*oid))) == NULL) { yyerror("calloc"); free($1); YYERROR; } - if (ober_string2oid($1, sysoid) == -1) { + if (smi_string2oid($1, oid) == -1) { yyerror("invalid OID: %s", $1); - free(sysoid); + free(oid); free($1); YYERROR; } free($1); - $$ = sysoid; + $$ = oid; } ;
snmpd(8): Add blocklist feature
Back in 2020 florian@ added the filter-pf-addresses keyword. Although useful, I always felt it was a bit too case-specific. The diff below adds a new blocklist feature/backend, which takes hold of an entire subtree, effectively removing it from the tree. With this I've deprecated the filter-pf-address case and should probably be removed somewhere after 7.4. The filter-routes case can't be removed unfortunately, since its behaviour is not identical, and instead adds filters to the routing socket, preventing updates being pushed to snmpd(8). Feedback/OK? martijn@ Index: Makefile === RCS file: /cvs/src/usr.sbin/snmpd/Makefile,v retrieving revision 1.18 diff -u -p -r1.18 Makefile --- Makefile19 Jan 2022 11:00:56 - 1.18 +++ Makefile28 Jun 2022 08:18:17 - @@ -3,6 +3,7 @@ PROG= snmpd MAN= snmpd.8 snmpd.conf.5 SRCS= parse.y log.c snmpe.c application.c application_legacy.c \ + application_blocklist.c \ mps.c trap.c mib.c smi.c kroute.c snmpd.c timer.c \ pf.c proc.c usm.c traphandler.c util.c Index: application.c === RCS file: /cvs/src/usr.sbin/snmpd/application.c,v retrieving revision 1.5 diff -u -p -r1.5 application.c --- application.c 27 Jun 2022 10:31:17 - 1.5 +++ application.c 28 Jun 2022 08:18:17 - @@ -148,6 +148,7 @@ RB_PROTOTYPE_STATIC(appl_requests, appl_ void appl_init(void) { + appl_blocklist_init(); appl_legacy_init(); } Index: application.h === RCS file: /cvs/src/usr.sbin/snmpd/application.h,v retrieving revision 1.1 diff -u -p -r1.1 application.h --- application.h 19 Jan 2022 10:59:35 - 1.1 +++ application.h 28 Jun 2022 08:18:17 - @@ -133,3 +133,6 @@ struct ber_element *appl_exception(enum /* application_legacy.c */ voidappl_legacy_init(void); voidappl_legacy_shutdown(void); + +/* application_blocklist.c */ +voidappl_blocklist_init(void); Index: application_blocklist.c === RCS file: application_blocklist.c diff -N application_blocklist.c --- /dev/null 1 Jan 1970 00:00:00 - +++ application_blocklist.c 28 Jun 2022 08:18:17 - @@ -0,0 +1,122 @@ +/* $OpenBSD: application.c,v 1.5 2022/06/27 10:31:17 martijn Exp $ */ + +/* + * Copyright (c) 2022 Martijn van Duren + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include + +#include "application.h" +#include "snmpd.h" + +struct appl_varbind *appl_blocklist_response(size_t); +void appl_blocklist_get(struct appl_backend *, int32_t, int32_t, const char *, +struct appl_varbind *); +void appl_blocklist_getnext(struct appl_backend *, int32_t, int32_t, +const char *, struct appl_varbind *); + +struct appl_backend_functions appl_blocklist_functions = { + .ab_get = appl_blocklist_get, + .ab_getnext = appl_blocklist_getnext, + .ab_getbulk = NULL, +}; + +struct appl_backend appl_blocklist = { + .ab_name = "blocklist", + .ab_cookie = NULL, + .ab_retries = 0, + .ab_fn = &appl_blocklist_functions +}; + +static struct appl_varbind *response = NULL; +static size_t responsesz = 0; + +struct appl_varbind * +appl_blocklist_response(size_t nvarbind) +{ + struct appl_varbind *tmp; + size_t i; + + if (responsesz < nvarbind) { + if ((tmp = recallocarray(response, responsesz, nvarbind, + sizeof(*response))) == NULL) { + log_warn(NULL); + return NULL; + } + responsesz = nvarbind; + response = tmp; + } + for (i = 0; i < nvarbind; i++) + response[i].av_next = i + 1 == nvarbind ? + NULL : &(response[i + 1]); + return response; +} + +void +appl_blocklist_init(void) +{ + extern struct snmpd *snmpd_env; + size_t i; + + for (i = 0; i < snmpd_env->sc_nblocklist; i++) + appl_register(NULL,
snmpd(8): Add rudimentary AgentX support
13 @@ appl_response(struct appl_backend *backe log_warnx("Invalid error index"); invalid = 1; } +/* amavisd-snmp-subagent sets index to 1, no reason to crash over it. */ +#if PEDANTIC if (error == APPL_ERROR_NOERROR && index != 0) { log_warnx("error index with no error"); invalid = 1; } +#endif if (vb == NULL && origvb != NULL) { log_warnx("%s: Request %"PRIu32" returned less varbinds then " "requested", backend->ab_name, requestid); Index: usr.sbin/snmpd.application.agentx/application.h === RCS file: /cvs/src/usr.sbin/snmpd/application.h,v retrieving revision 1.1 diff -u -p -r1.1 application.h --- usr.sbin/snmpd.application.agentx/application.h 19 Jan 2022 10:59:35 - 1.1 +++ usr.sbin/snmpd.application.agentx/application.h 27 Jun 2022 11:29:36 - @@ -117,6 +117,7 @@ struct appl_backend { RB_HEAD(appl_requests, appl_request_downstream) ab_requests; }; +void appl(void); void appl_init(void); void appl_shutdown(void); enum appl_error appl_register(const char *, uint32_t, uint8_t, struct ber_oid *, @@ -133,3 +134,8 @@ struct ber_element *appl_exception(enum /* application_legacy.c */ voidappl_legacy_init(void); voidappl_legacy_shutdown(void); + +/* application_agentx.c */ +voidappl_agentx(void); +voidappl_agentx_init(void); +voidappl_agentx_shutdown(void); Index: usr.sbin/snmpd.application.agentx/application_agentx.c === RCS file: usr.sbin/snmpd.application.agentx/application_agentx.c diff -N usr.sbin/snmpd.application.agentx/application_agentx.c --- /dev/null 1 Jan 1970 00:00:00 - +++ usr.sbin/snmpd.application.agentx/application_agentx.c 27 Jun 2022 11:29:36 - @@ -0,0 +1,842 @@ +/* $OpenBSD$ */ +/* + * Copyright (c) 2021 Martijn van Duren + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "application.h" +#include "ax.h" +#include "log.h" +#include "smi.h" +#include "snmp.h" +#include "snmpd.h" + +#define AGENTX_DEFAULTTIMEOUT 5 + +struct appl_agentx { + uint32_t ax_id; + struct ax *ax_ax; + struct event ax_ev; + + struct appl_agentx_session *ax_session; /* List of sessions */ + RB_ENTRY(appl_agentx) ax_entry; +}; + +struct appl_agentx_session { + struct appl_agentx *axs_ax; + uint32_t axs_id; + enum ax_byte_order axs_byteorder; + uint8_t axs_timeout; + struct ax_oid axs_oid; + struct ax_ostring axs_descr; + struct appl_backend axs_backend; + + RB_ENTRY(appl_agentx_session) axs_entry; + struct appl_agentx_session *axs_next; +}; + +void appl_agentx_listen(struct agentx_master *); +void appl_agentx_accept(int, short, void *); +void appl_agentx_free(struct appl_agentx *); +void appl_agentx_read(int, short, void *); +void appl_agentx_open(struct appl_agentx *, struct ax_pdu *); +void appl_agentx_close(struct appl_agentx_session *, struct ax_pdu *); +void appl_agentx_forceclose(struct appl_backend *, enum appl_close_reason); +void appl_agentx_session_free(struct appl_agentx_session *); +void appl_agentx_register(struct appl_agentx_session *, struct ax_pdu *); +void appl_agentx_unregister(struct appl_agentx_session *, struct ax_pdu *); +void appl_agentx_get(struct appl_backend *, int32_t, int32_t, const char *, +struct appl_varbind *); +void appl_agentx_getnext(struct appl_backend *, int32_t, int32_t, const char *, +struct appl_varbind *); +void appl_agentx_response(struct appl_agentx_session *, struct ax_pdu *); +ssize_t appl_agentx_send(struct appl_agentx_session *); +struct ber_oid *appl_agentx_oid2ber_oid(struct ax_oid *, struct ber_oid *); +struct ber_element *appl_agentx_value2ber_element(struct ax_varbind *); +struct ax_ostring *appl_agentx_string2ostring(const char *, +struct ax_ostring *); +int appl_agentx_cmp(struct
snmpd(8): Application.c properly initialize oidbuf for appl_region
When registering a region in appl_region (through appl_register) we fill oidbuf with strlcat, but we don't start from a clean state and might have garbage prepended. This oidbuf is only used on error-conditions, so it's unlikely to trigger with the current code. Diff below properly initializes it. OK? martijn@ Index: application.c === RCS file: /cvs/src/usr.sbin/snmpd/application.c,v retrieving revision 1.3 diff -u -p -r1.3 application.c --- application.c 22 Feb 2022 15:59:13 - 1.3 +++ application.c 27 Jun 2022 10:10:37 - @@ -224,6 +224,7 @@ appl_region(struct appl_context *ctx, ui goto overlap; /* Don't use smi_oid2string, because appl_register can't use it */ + oidbuf[0] = '\0'; for (i = 0; i < oid->bo_n; i++) { if (i != 0) strlcat(oidbuf, ".", sizeof(oidbuf));
Re: syslogd(8): Add hostname parsing support
On Wed, 2022-01-26 at 09:18 -0700, Theo de Raadt wrote: > > However, as things stand interpretation can be broken with the base > > tools. I can't fix garbage input. > > Your proposal builds a mechanism which encourages making decisions based > upon parsing garbage input. So let's just focus on my original diff and let that one stand on its own. Say I have a sender with the following line: sender$ tail -2 /etc/syslog.conf !doas *.* @100.64.2.2 and a receiver with: receiver$ tail -6 /etc/syslog.conf +100.64.2.3 !doas *.* /tmp/prog_doas !sender *.* /tmp/prog_sender receiver runs syslogd with -U100.64.2.2 When running syslogd on sender without any flags and doing a simple `doas ls`: receiver$ tail /tmp/prog_* ==> /tmp/prog_doas <== Jan 30 15:03:51 100.64.2.3 doas: martijn ran command ls as root from /home/martijn ==> /tmp/prog_sender <== receiver$ When running syslogd on sender with -h and doing a simple `doas ls`: receiver$ tail /tmp/prog_* ==> /tmp/prog_doas <== ==> /tmp/prog_sender <== Jan 30 15:08:08 100.64.2.3 sender doas: martijn ran command ls as root from /home/martijn receiver$ With my diff applied and without -h: receiver$ tail /tmp/prog_* ==> /tmp/prog_doas <== Jan 30 15:11:55 100.64.2.3 doas: martijn ran command ls as root from /home/martijn ==> /tmp/prog_sender <== receiver$ With my diff applied and with -h: receiver$ tail /tmp/prog_* ==> /tmp/prog_doas <== Jan 30 15:12:47 100.64.2.3 doas: martijn ran command ls as root from /home/martijn ==> /tmp/prog_sender <== receiver$ So no new mechanism is introduced, but I am trying to make sure that existing mechanisms work more consistent with what we already offer out of the box in base. martijn@ Index: parsemsg.c === RCS file: /cvs/src/usr.sbin/syslogd/parsemsg.c,v retrieving revision 1.1 diff -u -p -r1.1 parsemsg.c --- parsemsg.c 13 Jan 2022 10:34:07 - 1.1 +++ parsemsg.c 30 Jan 2022 14:20:36 - @@ -17,8 +17,14 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include + +#include +#include + #include #include +#include #include #include #include @@ -29,27 +35,42 @@ size_t parsemsg_timestamp_bsd(const char *, char *); size_t parsemsg_timestamp_v1(const char *, char *); +size_t parsemsg_hostname(const char *, char *); size_t parsemsg_prog(const char *, char *); struct msg * parsemsg(const char *msgstr, struct msg *msg) { - size_t n; + size_t timelen, proglen; + const char *hostname; msg->m_pri = -1; msgstr += parsemsg_priority(msgstr, &msg->m_pri); if (msg->m_pri &~ (LOG_FACMASK|LOG_PRIMASK)) msg->m_pri = -1; - if ((n = parsemsg_timestamp_bsd(msgstr, msg->m_timestamp)) == 0) - n = parsemsg_timestamp_v1(msgstr, msg->m_timestamp); - msgstr += n; + if ((timelen = parsemsg_timestamp_bsd(msgstr, msg->m_timestamp)) == 0) + timelen = parsemsg_timestamp_v1(msgstr, msg->m_timestamp); + msgstr += timelen; - while (isspace(msgstr[0])) + while (isspace((unsigned char)msgstr[0])) msgstr++; - parsemsg_prog(msgstr, msg->m_prog); + hostname = msgstr; + msgstr += parsemsg_hostname(msgstr, msg->m_hostname); + + while (isspace((unsigned char)msgstr[0])) + msgstr++; + proglen = parsemsg_prog(msgstr, msg->m_prog); + + /* +* Without timestamp and tag, assume hostname as part of message. +*/ + if (!timelen && !proglen) { + msg->m_hostname[0] = '\0'; + msgstr = hostname; + } strlcpy(msg->m_msg, msgstr, sizeof(msg->m_msg)); return msg; @@ -169,6 +190,47 @@ parsemsg_timestamp_v1(const char *msgstr return msg - msgstr; } +/* + * Parse the ip address or hostname according to inet_pton and res_hnok and + * return the length of the hostname including the trailing space if available. + */ +size_t +parsemsg_hostname(const char *msgstr, char *hostname) +{ + size_t len; + struct in_addr buf4; + struct in6_addr buf6; + + if (msgstr[0] == '-' && (msgstr[1] == ' ' || msgstr[1] == '\0')) { + hostname[0] = '\0'; + if (msgstr[1] == '\0') + return 1; + return 2; + } + + if ((len = strcspn(msgstr, " ")) > HOST_NAME_MAX) + return 0; + strlcpy(hostname, msgstr, len + 1); + if (msgstr[len] == ' ') + len++; + + if (inet_pton(AF_INET, hostname, &buf4) == 1 || + inet_pton(AF_INET6, hostname, &buf6) == 1) { + return len; + } + + if (res_hnok(hostname) == 0) { + hostname[0] = '\0'; + return 0; + } +
Re: snmpd(8): clean up variable printing
On Wed, 2022-01-19 at 16:23 +0100, Martijn van Duren wrote: > The new code uses smi_print_element when debugging is enabled to trace > calls. Unfortunately the current smi_print_element lacks in quite a few > departments. This diff rewrites smi_print_element to be more concise > than what we currently have, without moving into the more complex > territory that snmp(1) has. > > Unknown types are printed in a similar fashion to what tcpdump(8)'s > snmp output does. > > This change helps mostly with exceptions (NOSUCH{OBJECT,INSTANCE} and > ENDOFMIBVIEW) and distinguishing between different integer types. > > I kept the current implementation under smi_print_element_legacy, so > that we don't change the output of trap handlers. We should probably > revisit that one at some point, but I don't think to go into that > territory right now. > > OK? > > martijn@ > > p.s. I'm not particularly thrilled about the type hinting, but it's > the cleanest that I could come up with without being too much of an > eyesore or filling the screen up even further. Found a missing break after the BER_TYPE_NULL case. OK? martijn@ Index: smi.c === RCS file: /cvs/src/usr.sbin/snmpd/smi.c,v retrieving revision 1.30 diff -u -p -r1.30 smi.c --- smi.c 21 Oct 2021 15:08:15 - 1.30 +++ smi.c 29 Jan 2022 07:28:15 - @@ -46,6 +46,7 @@ #include "snmpd.h" #include "mib.h" +#include "application.h" #define MINIMUM(a, b) (((a) < (b)) ? (a) : (b)) @@ -461,8 +462,9 @@ smi_debug_elements(struct ber_element *r } #endif +/* Keep around so trap handle scripts don't break */ char * -smi_print_element(struct ber_element *root) +smi_print_element_legacy(struct ber_element *root) { char*str = NULL, *buf, *p; size_t len, i; @@ -520,6 +522,140 @@ smi_print_element(struct ber_element *ro case BER_TYPE_SET: default: str = strdup(""); + break; + } + + return (str); + + fail: + free(str); + return (NULL); +} + +char * +smi_print_element(struct ber_element *root) +{ + char*str = NULL, *buf, *p; + long longv; + struct ber_oid o; + char strbuf[BUFSIZ]; + + switch (root->be_class) { + case BER_CLASS_UNIVERSAL: + switch (root->be_type) { + case BER_TYPE_INTEGER: + if (ober_get_integer(root, &v) == -1) + goto fail; + if (asprintf(&str, "%lld", v) == -1) + goto fail; + break; + case BER_TYPE_OBJECT: + if (ober_get_oid(root, &o) == -1) + goto fail; + if (asprintf(&str, "%s", smi_oid2string(&o, strbuf, + sizeof(strbuf), 0)) == -1) + goto fail; + break; + case BER_TYPE_OCTETSTRING: + if (ober_get_string(root, &buf) == -1) + goto fail; + p = reallocarray(NULL, 4, root->be_len + 1); + if (p == NULL) + goto fail; + strvisx(p, buf, root->be_len, VIS_NL); + if (asprintf(&str, "\"%s\"", p) == -1) { + free(p); + goto fail; + } + free(p); + break; + case BER_TYPE_NULL: + if (asprintf(&str, "null") == -1) + goto fail; + break; + default: + /* Should not happen in a valid SNMP packet */ + if (asprintf(&str, "[U/%u]", root->be_type) == -1) + goto fail; + break; + } + break; + case BER_CLASS_APPLICATION: + switch (root->be_type) { + case SNMP_T_IPADDR: + if (ober_get_string(root, &buf) == -1) + goto fail; + if (asprintf(&str, "%s", + inet_ntoa(*(struct in_addr *)buf)) == -1) + goto fail; + break; + case SNMP_T_COUNTER32: + if (ober_get_integer(root, &v) == -1) + goto fail; + if (asprintf(&str, "
Re: syslogd(8): Add hostname parsing support
Thanks for looking into this. On Sat, 2022-01-22 at 10:05 -0700, Theo de Raadt wrote: > > Note that this only adds the parsing, the rest of the current behaviour > > of stays the same. I have another diff in the pipeline for allowing the > > hostname in the message. > > I object to this process. > > You want to add parsing code as a fait accompli. With no justification. > Then later on, spring on us the code that uses it. Sorry if my phrasing was off, but I do think the diff stands on its own and I thought I made that case in the first paragraph. Even though I include the mentioned follow up diffs below, let me try to rephrase my case for it as a stand alone diff. Right now we have 3 elements where syslog.conf can filter on: priority, hostname, and progname. hostname currently always comes from the input source: - sendsyslog(2), /dev/klog, unix socket, vlogmsg(), and markit() use LocalHostName - UDP/TCP/TLS print reverse lookup or numerical if -n is specified My previous parsemsg() diff didn't change any behaviour, but makes it more clear what's going on. When a message is handled through printline() it goes through parsemsg(). Here we first check for an optional priority and timestamp. parsemsg_priority() is the original priority parsing code and parsemsg_timestamp_{bsd,v1} are the original code lifted from logmsg. After that we continue to parsemsg_prog, which is also lifted from logmsg(). In other words, at no point do we look for a hostname in a message. So if someone were to set -h in syslogd or use any other syslog server that includes a hostname in the message and would relay it to a syslogd(8) it would first parse priority/timestamp (if available) and then continue to interpret the "first word" as progname. So a message like: "<13>Jan 25 20:35:44 myhost myprog: mymsg" send over localhost would have myhost as progname and localhost (or 127.0.0.1 if -n is used) as hostname. So if someone would have "!myprog" in their syslog.conf it would not match (while it should) and someone who would have "!myhost" in their syslog.conf would match, while it shouldn't. What my diff does is based mainly on syslog(3) (and happens to match what I've mostly seen in the wild) by saying that a progname should end in a ':' or a '['. This is also what NetBSD does. By this minor change we can reliably determine what the progname is and by that merit determine what the hostname is. The hostname in the original diff is stored in struct msg and not looked at again, effectively replacing the hostname with the address found by the input source. This behaviour should be identical to FreeBSD, which has the -H flag to preserve the hostname in the message. To make this explicit, the example message previously would be written out as: "<13>Jan 25 20:35:44 localhost myprog: mymsg" instead of the current "<13>Jan 25 20:35:44 localhost myhost myprog: mymsg" and "+localhost" would continue to keep working as always. > What if we disagree > with the code that uses it? Will you delete this parsing code which > nothing uses? Fixing the "!progname" case should be sufficient reason on its own and no other. > > > - Timestamp: is easy to interpret, since it's a strict format. > > No changes here. > > I believe "timestamp missing" is not strictly permitted. But this was > common for a while, and in OpenBSD it is the default message format. > This is a due to the desire to make syslog_r(3) be signal/re-entrant > safe on top of sendsyslog(2). Then there is no good way of creating a > timestamp string in the sender libc context. A timestamp is easily > re-inserted by the receiving syslogd. > > > + for (i = 0; i < HOST_NAME_MAX; i++) { > > Unlike MAXHOSTNAMELEN/NI_MAXHOST, HOST_NAME_MAX storage does not include > a NUL. You might have the loop right. Be careful. I've defined it as "char m_hostname[HOST_NAME_MAX + 1];", so we have room for the NUL. > > > +* fqdn: [[:alnum:]-.] > > That is not totally correct. > > hostnames very often also contain '_' in the middle positions, early > RFCs said no, but around 1990-ish Vixie in particular had to face > reality.. I was involved in that conversation, it seems so long ago. > > Your pattern is also incorrect in other ways, such as ".." is not legal, > hostnames cannot start or end with '-' or '-', etc. The current accepted > rules are encoded in the undocumented libc function __res_hnok in > lib/libc/net/res_comp.c I wasn't aware of res_hnok. I see that it's used in other applications as well. I changed it to use strcspn to look for the terminating space and try inet_pton and res_hnok to check if it's valid. This makes the code a lot cleaner. Thanks. > > I don't know if false-identification of broken hostnames is bad or not > I guess it depends what will happen with this information later on [ie. > the part of your proposal that is being kept a secret]. The same thing that already happens. Writing it out to log-files and using it to match on "+hostna
syslogd(8): Add hostname parsing support
Currently syslogd(8) doesn't support hostname parsing for incoming messages. This means that if a sender adds a hostname to a message it will be interpreted as progname. Additionally, when a message is being relayed, or there's some form of NATting taking place the originator of the message will be completely lost. The diff below adds hostname parsing and is already OK bluhm@, but since I wanted to give other people a chance to yell at me before committing. If nobody objects I'll commit it next weekend. Note that this only adds the parsing, the rest of the current behaviour of stays the same. I have another diff in the pipeline for allowing the hostname in the message. The definitions below are mostly in line with what {Net,Free}BSD do. The BSD syslog protocol is rather loose and in quite a few locations open for interpretation, so some definitions need to be hammered out: - Timestamp: is easy to interpret, since it's a strict format. No changes here. - Hostname: ip{,6} address, RFC1034 label, fqdn and is max HOST_NAME_MAX long. - progname: alphanumeric, '-', '.', '_', ending in a ':' or '[' and is max 255 characters long. This changes in that a progname must end with ':' or '['. I left the program name parsing as similar as I could, but make it always distinguishable from the hostname. The ending in ':' or '[' is part of syslog(3) and what I've always seen in the wild (ymmv). The only exception to this rule is when progname (or ident as syslog(3) calls it) fills up the message and ':' can't be placed. However, in that case it won't fit in syslogd(8)'s progname definition of 255 characters. According to syslog(3)'s guts, progname can be omitted if both ident and LOG_PID have been omitted during openlog(3), and __progname isn't set (no clue how that could happen op OpenBSD). If this somehow miraculously happens the whole message is just the "message" parameter of the syslog(3) call. Because progname is now always identifiable, we know if we have a hostname if progname is set and found on the first or second position after timestamp/priority. If progname is completely omitted we're a bit in no-mans-land. So here we have to make a best effort. Currently we never set the hostname when forwarding a message, unless syslogd is specified with '-h'. However, this is not a sane default since it hinders relaying messages and bluhm@ and I already agree that we should make -h default and make -h a noop. Going from syslog(3), if no timestamp is given we have just the message plus (not quite) optional progname/ident. So my proposal is that hostname should be parsed if either timestamp or progname is found. Finally, the syslogd regress with rsyslog now fails, because we send a message from the guts of the perl framework without timestamp or progname, but rsyslog adds a timestamp when forwarding, but no other changes are made. This test can be re-enabled when we allow using the hostname from the message, or someone is brave enough to go into these perl guts. Additional OK? Comment? martijn@ Index: usr.sbin/syslogd/parsemsg.c === RCS file: /cvs/src/usr.sbin/syslogd/parsemsg.c,v retrieving revision 1.1 diff -u -p -r1.1 parsemsg.c --- usr.sbin/syslogd/parsemsg.c 13 Jan 2022 10:34:07 - 1.1 +++ usr.sbin/syslogd/parsemsg.c 22 Jan 2022 14:52:04 - @@ -17,6 +17,11 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include + +#include +#include + #include #include #include @@ -29,27 +34,42 @@ size_t parsemsg_timestamp_bsd(const char *, char *); size_t parsemsg_timestamp_v1(const char *, char *); +size_t parsemsg_hostname(const char *, char *); size_t parsemsg_prog(const char *, char *); struct msg * parsemsg(const char *msgstr, struct msg *msg) { - size_t n; + size_t timelen, proglen; + const char *hostname; msg->m_pri = -1; msgstr += parsemsg_priority(msgstr, &msg->m_pri); if (msg->m_pri &~ (LOG_FACMASK|LOG_PRIMASK)) msg->m_pri = -1; - if ((n = parsemsg_timestamp_bsd(msgstr, msg->m_timestamp)) == 0) - n = parsemsg_timestamp_v1(msgstr, msg->m_timestamp); - msgstr += n; + if ((timelen = parsemsg_timestamp_bsd(msgstr, msg->m_timestamp)) == 0) + timelen = parsemsg_timestamp_v1(msgstr, msg->m_timestamp); + msgstr += timelen; + + while (isspace((unsigned char)msgstr[0])) + msgstr++; - while (isspace(msgstr[0])) + hostname = msgstr; + msgstr += parsemsg_hostname(msgstr, msg->m_hostname); + + while (isspace((unsigned char)msgstr[0])) msgstr++; - parsemsg_prog(msgstr, msg->m_prog); + proglen = parsemsg_prog(msgstr, msg->m_prog); + /* +* Without timestamp and tag, assume hostname as part of message. +*/ + if (!timelen && !proglen) { + msg->m_hostname[0] = '\0'; +
Re: snmpd(8): fix exceptions in mps.c
Disregard this one for now. If o_get returns -1 it indicates an error, so it should indicate this to the upper layers. However, the old code can't handle this and I kept this code as is so that we can have some time to let the dust settle around the new code (and easily switch back if needed). Changing it to something more in line with the intended behaviour of the current code, but still incorrect is not really an improvement. I'll revisit this one once more people have been exposed to the new code. On Thu, 2022-01-20 at 22:08 +0100, Martijn van Duren wrote: > When hitting an error case in mps_get{,next}req, mps assumes that no OID > has been linked to the root element. However, in both the get as well as > the getnext case it's already set when entering the mib.c code, so going > the fail goto path will result in the intended OID/exception pair being > appended to the prior attached OID, instead of directly to the sequence > (OID,OID,EXCEPTION). > > This behaviour could already be triggered in the original codepath, so > it's not a new issue introduced with the new application.c. And even > though I would like to get rid of this code, I think it's worth fixing > while we still have it around. > > Easiest way to reproduce is to just return -1 in mib_getsys and do > get syscontact.0 or getnext syscontact. > > OK? > > martijn@ > > Index: mps.c > === > RCS file: /cvs/src/usr.sbin/snmpd/mps.c,v > retrieving revision 1.29 > diff -u -p -r1.29 mps.c > --- mps.c 30 Jun 2020 17:11:49 - 1.29 > +++ mps.c 20 Jan 2022 21:03:17 - > @@ -150,7 +150,9 @@ fail: > return (-1); > > /* Set SNMPv2 extended error response. */ > - elm = ober_add_oid(elm, o); > + if (root->be_sub != NULL) > + ober_free_elements(ober_unlink_elements(root)); > + elm = ober_add_oid(root, o); > elm = ober_add_null(elm); > ober_set_header(elm, BER_CLASS_CONTEXT, error_type); > return (0); > @@ -178,15 +180,16 @@ mps_setreq(struct snmp_message *msg, str > > int > mps_getnextreq(struct snmp_message *msg, struct ber_element *root, > -struct ber_oid *o) > +struct ber_oid *o0) > { > struct oid *next = NULL; > struct ber_element *ber = root; > struct oid key, *value; > int ret; > - struct ber_oid no; > - unsigned longerror_type = 0;/* noSuchObject */ > + struct ber_oid no, so, *o = &so; > + unsigned longerror_type = 2;/* EndOfMibView */ > > + so = *o0; > if (o->bo_n > BER_MAX_OID_LEN) > return (-1); > bzero(&key, sizeof(key)); > @@ -259,7 +262,9 @@ fail: > return (-1); > > /* Set SNMPv2 extended error response. */ > - ber = ober_add_oid(ber, o); > + if (root->be_sub != NULL) > + ober_free_elements(ober_unlink_elements(root)); > + ber = ober_add_oid(root, o0); > ber = ober_add_null(ber); > ober_set_header(ber, BER_CLASS_CONTEXT, error_type); > return (0); >
application.c be more paranoid for misbehaving backends
There's a missing NULL check in appl_response(). This should only happenwhen a backend is misbehaving, so I only managed to find this because I'm actively bashing it right now. This should make us a little more future-proof. Code further down the path already has similar NULL checks against this variable. OK? martijn@ Index: application.c === RCS file: /cvs/src/usr.sbin/snmpd/application.c,v retrieving revision 1.1 diff -u -p -r1.1 application.c --- application.c 19 Jan 2022 10:59:35 - 1.1 +++ application.c 20 Jan 2022 21:52:41 - @@ -1056,7 +1056,8 @@ appl_response(struct appl_backend *backe appl_varbind_error(origvb, error); origvb->avi_state = APPL_VBSTATE_DONE; origvb->avi_varbind.av_oid = vb->av_oid; - if (vb->av_value->be_class == BER_CLASS_CONTEXT && + if (vb->av_value != NULL && + vb->av_value->be_class == BER_CLASS_CONTEXT && vb->av_value->be_type == APPL_EXC_ENDOFMIBVIEW) { nregion = appl_region_next(ureq->aru_ctx, &(vb->av_oid), origvb->avi_region);
snmpd(8): fix exceptions in mps.c
When hitting an error case in mps_get{,next}req, mps assumes that no OID has been linked to the root element. However, in both the get as well as the getnext case it's already set when entering the mib.c code, so going the fail goto path will result in the intended OID/exception pair being appended to the prior attached OID, instead of directly to the sequence (OID,OID,EXCEPTION). This behaviour could already be triggered in the original codepath, so it's not a new issue introduced with the new application.c. And even though I would like to get rid of this code, I think it's worth fixing while we still have it around. Easiest way to reproduce is to just return -1 in mib_getsys and do get syscontact.0 or getnext syscontact. OK? martijn@ Index: mps.c === RCS file: /cvs/src/usr.sbin/snmpd/mps.c,v retrieving revision 1.29 diff -u -p -r1.29 mps.c --- mps.c 30 Jun 2020 17:11:49 - 1.29 +++ mps.c 20 Jan 2022 21:03:17 - @@ -150,7 +150,9 @@ fail: return (-1); /* Set SNMPv2 extended error response. */ - elm = ober_add_oid(elm, o); + if (root->be_sub != NULL) + ober_free_elements(ober_unlink_elements(root)); + elm = ober_add_oid(root, o); elm = ober_add_null(elm); ober_set_header(elm, BER_CLASS_CONTEXT, error_type); return (0); @@ -178,15 +180,16 @@ mps_setreq(struct snmp_message *msg, str int mps_getnextreq(struct snmp_message *msg, struct ber_element *root, -struct ber_oid *o) +struct ber_oid *o0) { struct oid *next = NULL; struct ber_element *ber = root; struct oid key, *value; int ret; - struct ber_oid no; - unsigned longerror_type = 0;/* noSuchObject */ + struct ber_oid no, so, *o = &so; + unsigned longerror_type = 2;/* EndOfMibView */ + so = *o0; if (o->bo_n > BER_MAX_OID_LEN) return (-1); bzero(&key, sizeof(key)); @@ -259,7 +262,9 @@ fail: return (-1); /* Set SNMPv2 extended error response. */ - ber = ober_add_oid(ber, o); + if (root->be_sub != NULL) + ober_free_elements(ober_unlink_elements(root)); + ber = ober_add_oid(root, o0); ber = ober_add_null(ber); ober_set_header(ber, BER_CLASS_CONTEXT, error_type); return (0);
Re: ober_get_writebuf return correct length
Forgot to mention, I checked all the instances of ober_get_writebuf I could find and they either don't use it for the actual length, or ber has been freshly initialised just before. So there's no problem here in the known consumers. On Thu, 2022-01-20 at 18:50 +0100, Martijn van Duren wrote: > While reading through ber.c I noticed that ober_get_writebuf can return > the wrong length when called multiple times on the same ber instance. > > This is because ober_get_writebuf uses br_wend to calculate the length, > while ober_write_elements uses that to determine the size of the buffer. > ober_write_elements uses br_wptr to determine how much has been written. > So use that pointer instead. > > $ cat test.c > #include > #include > #include > > int > main(int argc, char *argv[]) > { > struct ber_element *root; > struct ber ber; > void *buf; > > bzero(&ber, sizeof(ber)); > > root = ober_printf_elements(NULL, "{}"); > printf("%zd\n", ober_write_elements(&ber, root)); > printf("%zd\n", ober_get_writebuf(&ber, &buf)); > > ober_free_elements(root); > root = ober_printf_elements(NULL, "{d}", (int)1); > printf("%zd\n", ober_write_elements(&ber, root)); > printf("%zd\n", ober_get_writebuf(&ber, &buf)); > > ober_free_elements(root); > root = ober_printf_elements(NULL, "{}"); > printf("%zd\n", ober_write_elements(&ber, root)); > printf("%zd\n", ober_get_writebuf(&ber, &buf)); > } > $ CFLAGS='-lutil' make test && ./test > cc -lutil -o test test.c > 2 > 2 > 5 > 5 > 2 > 5 > > OK? > > martijn@ > > Index: ber.c > === > RCS file: /cvs/src/lib/libutil/ber.c,v > retrieving revision 1.23 > diff -u -p -r1.23 ber.c > --- ber.c 21 Oct 2021 08:17:33 - 1.23 > +++ ber.c 20 Jan 2022 17:48:27 - > @@ -831,7 +831,7 @@ ober_get_writebuf(struct ber *b, void ** > if (b->br_wbuf == NULL) > return -1; > *buf = b->br_wbuf; > - return (b->br_wend - b->br_wbuf); > + return (b->br_wptr - b->br_wbuf); > } > > /* >
ober_get_writebuf return correct length
While reading through ber.c I noticed that ober_get_writebuf can return the wrong length when called multiple times on the same ber instance. This is because ober_get_writebuf uses br_wend to calculate the length, while ober_write_elements uses that to determine the size of the buffer. ober_write_elements uses br_wptr to determine how much has been written. So use that pointer instead. $ cat test.c #include #include #include int main(int argc, char *argv[]) { struct ber_element *root; struct ber ber; void *buf; bzero(&ber, sizeof(ber)); root = ober_printf_elements(NULL, "{}"); printf("%zd\n", ober_write_elements(&ber, root)); printf("%zd\n", ober_get_writebuf(&ber, &buf)); ober_free_elements(root); root = ober_printf_elements(NULL, "{d}", (int)1); printf("%zd\n", ober_write_elements(&ber, root)); printf("%zd\n", ober_get_writebuf(&ber, &buf)); ober_free_elements(root); root = ober_printf_elements(NULL, "{}"); printf("%zd\n", ober_write_elements(&ber, root)); printf("%zd\n", ober_get_writebuf(&ber, &buf)); } $ CFLAGS='-lutil' make test && ./test cc -lutil -o test test.c 2 2 5 5 2 5 OK? martijn@ Index: ber.c === RCS file: /cvs/src/lib/libutil/ber.c,v retrieving revision 1.23 diff -u -p -r1.23 ber.c --- ber.c 21 Oct 2021 08:17:33 - 1.23 +++ ber.c 20 Jan 2022 17:48:27 - @@ -831,7 +831,7 @@ ober_get_writebuf(struct ber *b, void ** if (b->br_wbuf == NULL) return -1; *buf = b->br_wbuf; - return (b->br_wend - b->br_wbuf); + return (b->br_wptr - b->br_wbuf); } /*
remove snmpe.c transactionid
This was from a sequence of early attempts to work towards a new application layer. I can give more reasoning behind it, but the bottom line is that it's currently dead weight. OK to remove this code again? martijn@ Index: snmpd.h === RCS file: /cvs/src/usr.sbin/snmpd/snmpd.h,v retrieving revision 1.102 diff -u -p -r1.102 snmpd.h --- snmpd.h 19 Jan 2022 10:25:04 - 1.102 +++ snmpd.h 20 Jan 2022 13:38:28 - @@ -403,8 +403,6 @@ struct snmp_message { u_int8_t sm_data[READ_BUF_SIZE]; size_t sm_datalen; - uint32_t sm_transactionid; - u_intsm_version; /* V1, V2c */ @@ -441,11 +439,7 @@ struct snmp_message { struct ber_element *sm_varbind; struct ber_element *sm_varbindresp; - - RB_ENTRY(snmp_message) sm_entry; }; -RB_HEAD(snmp_messages, snmp_message); -extern struct snmp_messages snmp_messages; /* Defined in SNMPv2-MIB.txt (RFC 3418) */ struct snmp_stats { @@ -644,8 +638,6 @@ void snmpe(struct privsep *, struct pr voidsnmpe_shutdown(void); voidsnmpe_dispatchmsg(struct snmp_message *); voidsnmpe_response(struct snmp_message *); -int snmp_messagecmp(struct snmp_message *, struct snmp_message *); -RB_PROTOTYPE(snmp_messages, snmp_message, sm_entry, snmp_messagecmp) /* trap.c */ voidtrap_init(void); Index: snmpe.c === RCS file: /cvs/src/usr.sbin/snmpd/snmpe.c,v retrieving revision 1.82 diff -u -p -r1.82 snmpe.c --- snmpe.c 19 Jan 2022 11:00:56 - 1.82 +++ snmpe.c 20 Jan 2022 13:38:28 - @@ -58,8 +58,6 @@ intsnmpe_encode(struct snmp_message *) struct imsgev *iev_parent; static const struct timevalsnmpe_tcp_timeout = { 10, 0 }; /* 10s */ -struct snmp_messages snmp_messages = RB_INITIALIZER(&snmp_messages); - static struct privsep_proc procs[] = { { "parent", PROC_PARENT } }; @@ -246,11 +244,6 @@ snmpe_parse(struct snmp_message *msg) msg->sm_errstr = "invalid message"; - do { - msg->sm_transactionid = arc4random(); - } while (msg->sm_transactionid == 0 || - RB_INSERT(snmp_messages, &snmp_messages, msg) != NULL); - if (ober_scanf_elements(root, "{ie", &ver, &a) != 0) goto parsefail; @@ -910,8 +903,6 @@ snmpe_response(struct snmp_message *msg) void snmp_msgfree(struct snmp_message *msg) { - if (msg->sm_transactionid != 0) - RB_REMOVE(snmp_messages, &snmp_messages, msg); event_del(&msg->sm_sockev); ober_free(&msg->sm_ber); if (msg->sm_req != NULL) @@ -974,12 +965,3 @@ snmpe_encode(struct snmp_message *msg) #endif return 0; } - -int -snmp_messagecmp(struct snmp_message *m1, struct snmp_message *m2) -{ - return (m1->sm_transactionid < m2->sm_transactionid ? -1 : - m1->sm_transactionid > m2->sm_transactionid); -} - -RB_GENERATE(snmp_messages, snmp_message, sm_entry, snmp_messagecmp)
snmpd(8): clean up variable printing
The new code uses smi_print_element when debugging is enabled to trace calls. Unfortunately the current smi_print_element lacks in quite a few departments. This diff rewrites smi_print_element to be more concise than what we currently have, without moving into the more complex territory that snmp(1) has. Unknown types are printed in a similar fashion to what tcpdump(8)'s snmp output does. This change helps mostly with exceptions (NOSUCH{OBJECT,INSTANCE} and ENDOFMIBVIEW) and distinguishing between different integer types. I kept the current implementation under smi_print_element_legacy, so that we don't change the output of trap handlers. We should probably revisit that one at some point, but I don't think to go into that territory right now. OK? martijn@ p.s. I'm not particularly thrilled about the type hinting, but it's the cleanest that I could come up with without being too much of an eyesore or filling the screen up even further. Index: smi.c === RCS file: /cvs/src/usr.sbin/snmpd/smi.c,v retrieving revision 1.30 diff -u -p -r1.30 smi.c --- smi.c 21 Oct 2021 15:08:15 - 1.30 +++ smi.c 19 Jan 2022 15:21:20 - @@ -46,6 +46,7 @@ #include "snmpd.h" #include "mib.h" +#include "application.h" #define MINIMUM(a, b) (((a) < (b)) ? (a) : (b)) @@ -461,8 +462,9 @@ smi_debug_elements(struct ber_element *r } #endif +/* Keep around so trap handle scripts don't break */ char * -smi_print_element(struct ber_element *root) +smi_print_element_legacy(struct ber_element *root) { char*str = NULL, *buf, *p; size_t len, i; @@ -520,6 +522,139 @@ smi_print_element(struct ber_element *ro case BER_TYPE_SET: default: str = strdup(""); + break; + } + + return (str); + + fail: + free(str); + return (NULL); +} + +char * +smi_print_element(struct ber_element *root) +{ + char*str = NULL, *buf, *p; + long longv; + struct ber_oid o; + char strbuf[BUFSIZ]; + + switch (root->be_class) { + case BER_CLASS_UNIVERSAL: + switch (root->be_type) { + case BER_TYPE_INTEGER: + if (ober_get_integer(root, &v) == -1) + goto fail; + if (asprintf(&str, "%lld", v) == -1) + goto fail; + break; + case BER_TYPE_OBJECT: + if (ober_get_oid(root, &o) == -1) + goto fail; + if (asprintf(&str, "%s", smi_oid2string(&o, strbuf, + sizeof(strbuf), 0)) == -1) + goto fail; + break; + case BER_TYPE_OCTETSTRING: + if (ober_get_string(root, &buf) == -1) + goto fail; + p = reallocarray(NULL, 4, root->be_len + 1); + if (p == NULL) + goto fail; + strvisx(p, buf, root->be_len, VIS_NL); + if (asprintf(&str, "\"%s\"", p) == -1) { + free(p); + goto fail; + } + free(p); + break; + case BER_TYPE_NULL: + if (asprintf(&str, "null") == -1) + goto fail; + default: + /* Should not happen in a valid SNMP packet */ + if (asprintf(&str, "[U/%u]", root->be_type) == -1) + goto fail; + break; + } + break; + case BER_CLASS_APPLICATION: + switch (root->be_type) { + case SNMP_T_IPADDR: + if (ober_get_string(root, &buf) == -1) + goto fail; + if (asprintf(&str, "%s", + inet_ntoa(*(struct in_addr *)buf)) == -1) + goto fail; + break; + case SNMP_T_COUNTER32: + if (ober_get_integer(root, &v) == -1) + goto fail; + if (asprintf(&str, "%lld(c32)", v) == -1) + goto fail; + break; + case SNMP_T_GAUGE32: + if (ober_get_integer(root, &v) == -1) + goto fail; + if (asprintf(&str, "%lld(g32)", v) == -1) + goto fail; + break; + case SNMP_T_TIMETICKS: + if (ober_get_integer(root, &v) == -1) +
Re: sed(1): enable regression tests and correct pattern space assumptions
On Mon, 2022-01-10 at 08:21 -0700, Todd C. Miller wrote: > On Mon, 10 Jan 2022 15:23:42 +0100, Martijn van Duren wrote: > > > The lputs case is fairly straight forward and I'd like to get an OK > > for that part. > > I agree that fixing lputs() to honor psl is the best approach. > Wouldn't the diff be simpler if you left the handling of 's' as-is > but make the loop invariant l > 0 and decrement l accordingly? > Using len instead of l is probably a little easier to read. > > - todd sure Index: process.c === RCS file: /cvs/src/usr.bin/sed/process.c,v retrieving revision 1.34 diff -u -p -r1.34 process.c --- process.c 14 Nov 2018 10:59:33 - 1.34 +++ process.c 10 Jan 2022 15:52:26 - @@ -60,7 +60,7 @@ static SPACE HS, PS, SS; static inline int applies(struct s_command *); static void flush_appends(void); -static void lputs(char *); +static void lputs(char *, size_t); static inline int regexec_e(regex_t *, const char *, int, int, size_t, size_t); static void regsub(SPACE *, char *, char *); @@ -158,7 +158,7 @@ redirect: (void)fprintf(outfile, "%s", cp->t); break; case 'l': - lputs(ps); + lputs(ps, psl); break; case 'n': if (!nflag && !pd) @@ -478,14 +478,14 @@ flush_appends(void) } static void -lputs(char *s) +lputs(char *s, size_t len) { int count; extern int termwidth; const char *escapes; char *p; - for (count = 0; *s; ++s) { + for (count = 0; len > 0; len--, s++) { if (count >= termwidth) { (void)fprintf(outfile, "\\\n"); count = 0; @@ -501,7 +501,7 @@ lputs(char *s) } else { escapes = "\\\a\b\f\r\t\v"; (void)fputc('\\', outfile); - if ((p = strchr(escapes, *s))) { + if ((p = strchr(escapes, *s)) && *s != '\0') { (void)fputc("\\abfrtv"[p - escapes], outfile); count += 2; } else {
Re: sed(1): enable regression tests and correct pattern space assumptions
On Mon, 2022-01-10 at 00:25 -0600, user wrote: > The commandD1 regression test can be enabled without modifying sed's source > code, it is no longer failing. The commandl1, commandl2, and commandc1 tests > fail because the 'c' and 'D' commands assume that setting the correct length > of the pattern space is sufficient and do not correct the pattern space. > > The pattern space is cleared in the 'c' command by setting ps to a null byte > and a null byte is copied to the pattern space in the 'D' command to prevent > stray bytes from being printed by other commands. Specifically, the > substitute function in pattern.c:408 calls cspace(&SS, s++, 1, APPEND) if s > doesn't point to a null byte. If the pattern space isn't cleared by the 'c' > command this will be executed and cause the substitute space to be advanced > by 1 byte and causes an incorrect output (commandc1 test). If the pattern > space isn't cleared in the 'c' command lputs (called by the l command) will > also print the pattern space without checking the pattern space length > (commandl2 test), giving an incorrect output. In the 'D' command, the > original memmove did not copy the null byte at the end of the bytes that were > moved, causing a lputs call following the command to give an incorrect output > (commandl1 test). > > Not sure if the diff is correct since it was generated using got diff. Thanks for the detailed analysis. However, I think we should fix the cases where SPACE.len is not honoured... The lputs case is fairly straight forward and I'd like to get an OK for that part. The substitute case I haven't convinced myself just yet that it's correct. But (all) regress passes and I want to put it out here in case someone with more spare brain cycles than me wants to dive into it. martijn@ Index: process.c === RCS file: /cvs/src/usr.bin/sed/process.c,v retrieving revision 1.34 diff -u -p -r1.34 process.c --- process.c 14 Nov 2018 10:59:33 - 1.34 +++ process.c 10 Jan 2022 14:20:56 - @@ -60,7 +60,7 @@ static SPACE HS, PS, SS; static inline int applies(struct s_command *); static void flush_appends(void); -static void lputs(char *); +static void lputs(char *, size_t); static inline int regexec_e(regex_t *, const char *, int, int, size_t, size_t); static void regsub(SPACE *, char *, char *); @@ -158,7 +158,7 @@ redirect: (void)fprintf(outfile, "%s", cp->t); break; case 'l': - lputs(ps); + lputs(ps, psl); break; case 'n': if (!nflag && !pd) @@ -390,11 +390,11 @@ substitute(struct s_command *cp) * and at the end of the line, terminate. */ if (match[0].rm_so == match[0].rm_eo) { - if (*s == '\0' || *s == '\n') + if (slen == 0 || *s == '\0' || *s == '\n') slen = -1; else slen--; - if (*s != '\0') { + if (slen >= 0 && *s != '\0') { cspace(&SS, s++, 1, APPEND); le++; } @@ -478,34 +478,35 @@ flush_appends(void) } static void -lputs(char *s) +lputs(char *s, size_t l) { int count; extern int termwidth; const char *escapes; + size_t i; char *p; - for (count = 0; *s; ++s) { + for (i = 0, count = 0; i < l; i++) { if (count >= termwidth) { (void)fprintf(outfile, "\\\n"); count = 0; } - if (isascii((unsigned char)*s) && isprint((unsigned char)*s) - && *s != '\\') { - (void)fputc(*s, outfile); + if (isascii((unsigned char)s[i]) && isprint((unsigned char)s[i]) + && s[i] != '\\') { + (void)fputc(s[i], outfile); count++; - } else if (*s == '\n') { + } else if (s[i] == '\n') { (void)fputc('$', outfile); (void)fputc('\n', outfile); count = 0; } else { escapes = "\\\a\b\f\r\t\v"; (void)fputc('\\', outfile); - if ((p = strchr(escapes, *s))) { + if ((p = strchr(escapes, s[i])) && s[i] != '\0') { (void)fputc("\\abfrtv"[p - escapes], outfile); count += 2; } else { -
Re: rev(1): pull MB_CUR_MAX out of the hot loop
I fully agree with your reasoning and also prefer this one over the previous two diff. OK martijn@ On Sun, 2022-01-09 at 00:45 +0100, Ingo Schwarze wrote: > Hi, > > Martijn van Duren wrote on Sat, Jan 08, 2022 at 08:30:20AM +0100: > > > Why not go for the following diff? > > It has a comparable speed increase, but without the added complexity > > of a second inner loop. > > Actually, i like the idea of not duplicating the loop, in cases where > that is possible without a noticeable performance cost. > > I admit i OK'ed the original UTF-8 diff almost six years ago, > but looking at the code again, i now dislike how it is testing > every byte twice for no apparent reason. Even worse, i regard > the lack of I/O error checking on write operations as a bug. > Note that it does make sense to proceed to the next file on *read* > errors, whereas a *write* error should better be fatal. > > For those reason, and because i prefer versions of isu8cont() > that do not inspect the locale, i propose the following patch > instead. > > As an aside, i tried using fwrite(3) instead of the manual > putchar(*u) loop, but that is almost exactly as slow as repeatedly > calling MB_CUR_MAX, probably due to either locking or orientation > setting or some aspect of iov handling or buffering or more than > one of those; i refrained from profiling it. > > - 8< - schnipp - >8 - 8< - schnapp - >8 - > > Index: rev.c > === > RCS file: /cvs/src/usr.bin/rev/rev.c,v > retrieving revision 1.13 > diff -u -p -r1.13 rev.c > --- rev.c 10 Apr 2016 17:06:52 - 1.13 > +++ rev.c 8 Jan 2022 23:19:46 - > @@ -46,13 +46,14 @@ void usage(void); > int > main(int argc, char *argv[]) > { > - char *filename, *p = NULL, *t, *u; > + char *filename, *p = NULL, *t, *te, *u; > FILE *fp; > ssize_t len; > size_t ps = 0; > - int ch, rval; > + int ch, multibyte, rval; > > setlocale(LC_CTYPE, ""); > + multibyte = MB_CUR_MAX > 1; > > if (pledge("stdio rpath", NULL) == -1) > err(1, "pledge"); > @@ -83,14 +84,16 @@ main(int argc, char *argv[]) > if (p[len - 1] == '\n') > --len; > for (t = p + len - 1; t >= p; --t) { > - if (isu8cont(*t)) > - continue; > - u = t; > - do { > - putchar(*u); > - } while (isu8cont(*(++u))); > + te = t; > + if (multibyte) > + while (t > p && isu8cont(*t)) > + --t; > + for (u = t; u <= te; ++u) > + if (putchar(*u) == EOF) > + err(1, "stdout"); > } > - putchar('\n'); > + if (putchar('\n') == EOF) > + err(1, "stdout"); > } > if (ferror(fp)) { > warn("%s", filename); > @@ -104,7 +107,7 @@ main(int argc, char *argv[]) > int > isu8cont(unsigned char c) > { > - return MB_CUR_MAX > 1 && (c & (0x80 | 0x40)) == 0x80; > + return (c & (0x80 | 0x40)) == 0x80; > } > > void > > - 8< - schnipp - >8 - 8< - schnapp - >8 - > > The rest of this mail contains test reports. > These test reports use two test programs: > > - 8< - schnipp - >8 - 8< - schnapp - >8 - > > /* makeutf8.c */ > > #include > #include > #include > #include > #include > > int > main(void) > { > FILE*in, *out; > char*line = NULL; > size_t linesize = 0; > unsigned int wc, wcl = 0; > > if (setlocale(LC_CTYPE, "en_US.UTF-8") == NULL) > err(1, "setlocale"); > > if ((in = fopen("/usr/src/gnu/usr.bin/perl/lib/unicore/" > "UnicodeData.txt", "r")) == NULL) > err(1, "in"); > if ((out = fopen("utf8.txt", "w")) == NULL) > err(1, "out"); > > whil
Re: rev(1): pull MB_CUR_MAX out of the hot loop
On Fri, 2022-01-07 at 15:00 -0600, Scott Cheloha wrote: > On Fri, Jan 07, 2022 at 01:43:24PM -0600, Scott Cheloha wrote: > > > > [...] > > > > Like this? > > > > [...] > > Updated: make the for-loop update expressions match. > Why not go for the following diff? It has a comparable speed increase, but without the added complexity of a second inner loop. Either diff is fine by me though, so if you want to go ahead with your diff: OK martijn@ $ export LC_ALL=C $ for i in $(jot 10); do time rev /usr/share/dict/words > /dev/null; done 0m00.17s real 0m00.16s user 0m00.02s system 0m00.17s real 0m00.17s user 0m00.01s system 0m00.17s real 0m00.17s user 0m00.01s system 0m00.17s real 0m00.18s user 0m00.00s system 0m00.17s real 0m00.17s user 0m00.01s system 0m00.20s real 0m00.16s user 0m00.03s system 0m00.23s real 0m00.16s user 0m00.01s system 0m00.17s real 0m00.18s user 0m00.00s system 0m00.17s real 0m00.21s user 0m00.00s system 0m00.17s real 0m00.16s user 0m00.01s system $ for i in $(jot 10); do time ./obj/rev.scott /usr/share/dict/words > /dev/null; done 0m00.04s real 0m00.04s user 0m00.01s system 0m00.04s real 0m00.04s user 0m00.02s system 0m00.06s real 0m00.04s user 0m00.02s system 0m00.05s real 0m00.04s user 0m00.01s system 0m00.04s real 0m00.05s user 0m00.00s system 0m00.04s real 0m00.04s user 0m00.00s system 0m00.04s real 0m00.04s user 0m00.01s system 0m00.04s real 0m00.04s user 0m00.01s system 0m00.04s real 0m00.03s user 0m00.01s system 0m00.04s real 0m00.05s user 0m00.00s system $ for i in $(jot 10); do time ./obj/rev.martijn /usr/share/dict/words > /dev/null; done 0m00.04s real 0m00.04s user 0m00.01s system 0m00.04s real 0m00.04s user 0m00.01s system 0m00.04s real 0m00.04s user 0m00.01s system 0m00.04s real 0m00.03s user 0m00.01s system 0m00.04s real 0m00.04s user 0m00.00s system 0m00.04s real 0m00.06s user 0m00.00s system 0m00.04s real 0m00.04s user 0m00.00s system 0m00.04s real 0m00.05s user 0m00.00s system 0m00.04s real 0m00.04s user 0m00.01s system 0m00.04s real 0m00.04s user 0m00.01s system $ export LC_ALL=en_US.UTF-8 $ for i in $(jot 10); do time rev /usr/share/dict/words > /dev/null; done 0m00.17s real 0m00.17s user 0m00.00s system 0m00.17s real 0m00.18s user 0m00.00s system 0m00.17s real 0m00.17s user 0m00.00s system 0m00.17s real 0m00.18s user 0m00.01s system 0m00.17s real 0m00.17s user 0m00.01s system 0m00.17s real 0m00.18s user 0m00.00s system 0m00.17s real 0m00.17s user 0m00.00s system 0m00.17s real 0m00.17s user 0m00.02s system 0m00.17s real 0m00.17s user 0m00.01s system 0m00.17s real 0m00.18s user 0m00.00s system $ for i in $(jot 10); do time ./obj/rev.scott /usr/share/dict/words > /dev/null; done 0m00.04s real 0m00.04s user 0m00.00s system 0m00.04s real 0m00.04s user 0m00.01s system 0m00.04s real 0m00.05s user 0m00.00s system 0m00.04s real 0m00.04s user 0m00.00s system 0m00.04s real 0m00.04s user 0m00.00s system 0m00.04s real 0m00.04s user 0m00.00s system 0m00.04s real 0m00.04s user 0m00.00s system 0m00.04s real 0m00.04s user 0m00.01s system 0m00.04s real 0m00.04s user 0m00.01s system 0m00.04s real 0m00.04s user 0m00.00s system $ for i in $(jot 10); do time ./obj/rev.martijn /usr/share/dict/words > /dev/null; done 0m00.04s real 0m00.04s user 0m00.01s system 0m00.05s real 0m00.04s user 0m00.00s system 0m00.05s real 0m00.05s user 0m00.00s system 0m00.05s real 0m00.03s user 0m00.02s system 0m00.05s real 0m00.05s user 0m00.00s system 0m00.05s real 0m00.05s user 0m00.00s system 0m00.05s real 0m00.05s user 0m00.00s system 0m00.05s real 0m00.04s user 0m00.01s system 0m00.05s real 0m00.04s user 0m00.01s system 0m00.05s real 0m00.05s user 0m00.00s system martijn@ ps. I don't have your fancy nanotime. Where can I find that? Index: rev.c === RCS file: /cvs/src/usr.bin/rev/rev.c,v retrieving revision 1.13 diff -u -p -r1.13 rev.c --- rev.c 10 Apr 2016 17:06:52 - 1.13 +++ rev.c 8 Jan 2022 07:09:19 - @@ -43,6 +43,8 @@ int isu8cont(unsigned char); void usage(void); +int multibyte; + int main(int argc, char *argv[]) { @@ -53,6 +55,7 @@ main(int argc, char *argv[]) int ch, rval; setlocale(LC_CTYPE, ""); + multibyte = MB_CUR_MAX > 1; if (pledge("stdio rpath", NULL
Re: snmpd(8): New application layer - step towards agentx support
Problem found: The code was compiled on -stable, which I apparently misread. There's changes in libutil in current that this diff needs. Pending Joel's results: Anyone else wanting to chime in? On Mon, 2022-01-03 at 15:09 +0100, Joel Carnat wrote: > Hello, > > I have just patched my snmpd from -current ; everything else is > 7.0-stable. I'm not sure what happens but I use the same snmpd.conf and > connects to snmpd from another machine using > > # snmpwalk -v 3 -a SHA -A "changeme" -l authPriv -u telegraf \ > -x AES -X "changeme" server > > But using the patched snmpd, I get the following error: > mib_2 = No Such Object available on this agent at this OID. Using the > 7.0 version, it works perfectly. > > I can send full snmpd logs if you think it's usefull. > > Regards, > Joel C. > > On 1/3/22 13:57, Martijn van Duren wrote: > > On Sun, 2021-11-21 at 14:58 +0100, Martijn van Duren wrote: > > > On Sun, 2021-11-14 at 14:35 +, Stuart Henderson wrote: > > > > On 2021/11/14 11:49, Martijn van Duren wrote: > > > > > sthen@ found an issue when using this diff with netsnmp tools. > > > > > > > > > > The problem was that I put the requestID in the msgID, resulting > > > > > in a mismatch upon receiving the reply. The reason that snmp(1) > > > > > works is because msgID and requestID are the same. > > > > > Diff below fixes things. > > > > > > > > This version works for me, and the runtime increase with librenms > > > > fetches and polls (which use a mixture of get/bulkwalk) is acceptable > > > > (10% or so). > > > > > > > Anyone else put this through a test? I want to move forward with this. > > > > > > martijn@ > > > > > 2 month ping. > > So far I only have gotten test results from sthen@. > > Should I just put this in or is someone planning to actually look into > > the code? > > > > martijn@
Re: snmpd(8): New application layer - step towards agentx support
On Mon, 2022-01-03 at 15:09 +0100, Joel Carnat wrote: > Hello, > > I have just patched my snmpd from -current ; everything else is > 7.0-stable. I'm not sure what happens but I use the same snmpd.conf and > connects to snmpd from another machine using > > # snmpwalk -v 3 -a SHA -A "changeme" -l authPriv -u telegraf \ > -x AES -X "changeme" server > > But using the patched snmpd, I get the following error: > mib_2 = No Such Object available on this agent at this OID. Using the > 7.0 version, it works perfectly. > > I can send full snmpd logs if you think it's usefull. Thanks for testing, unfortunately I can't reproduce your error: martijn$ snmpwalk -v3 -a SHA -A test1234 -l authpriv -u testsha1 -x AES -X test1234 127.0.0.1 | head SNMPv2-MIB::sysDescr.0 = STRING: OpenBSD martijn.office.cloudvps.nl 7.0 GENERIC.MP#213 amd64 SNMPv2-MIB::sysObjectID.0 = OID: OPENBSD-BASE-MIB::openBSDDefaultObjectID SNMPv2-MIB::sysUpTime.0 = Timeticks: (344136) 0:57:21.36 SNMPv2-MIB::sysContact.0 = STRING: Martijn van Duren SNMPv2-MIB::sysName.0 = STRING: martijn SNMPv2-MIB::sysLocation.0 = STRING: SNMPv2-MIB::sysORLastChange.0 = Timeticks: (0) 0:00:00.00 SNMPv2-MIB::sysORIndex.1 = INTEGER: 1 SNMPv2-MIB::sysORIndex.2 = INTEGER: 2 SNMPv2-MIB::sysORIndex.3 = INTEGER: 3 Also, your report doesn't make sense to me: snmpwalk is from net-snmp, while the name mib_2 is used (incorrectly) by snmp(1). And "No Such Object" should not happen on getnext requests (which is what a walk does): Something must have gone really weird to end up in such an error path. Could you please provide a minimal proof of concept snmpd.conf with full client command and output to allow me to reproduce this? > > Regards, > Joel C. > > On 1/3/22 13:57, Martijn van Duren wrote: > > On Sun, 2021-11-21 at 14:58 +0100, Martijn van Duren wrote: > > > On Sun, 2021-11-14 at 14:35 +, Stuart Henderson wrote: > > > > On 2021/11/14 11:49, Martijn van Duren wrote: > > > > > sthen@ found an issue when using this diff with netsnmp tools. > > > > > > > > > > The problem was that I put the requestID in the msgID, resulting > > > > > in a mismatch upon receiving the reply. The reason that snmp(1) > > > > > works is because msgID and requestID are the same. > > > > > Diff below fixes things. > > > > > > > > This version works for me, and the runtime increase with librenms > > > > fetches and polls (which use a mixture of get/bulkwalk) is acceptable > > > > (10% or so). > > > > > > > Anyone else put this through a test? I want to move forward with this. > > > > > > martijn@ > > > > > 2 month ping. > > So far I only have gotten test results from sthen@. > > Should I just put this in or is someone planning to actually look into > > the code? > > > > martijn@
Re: ldap search vs ldapsearch
On Mon, 2021-11-08 at 09:51 +0100, Martijn van Duren wrote: > On Sat, 2021-11-06 at 03:11 -0400, Allan Streib wrote: > > On OpenBSD 7.0-release, comparing the output of OpenLDAP's > > ldapsearch(1) to ldap(1) search, the ldap(1) search output is > > missing the last attribute of each directory entry. > > > > e.g. from a directory I am working on at work: > > > > $ ldapsearch -LLL -x -H ldapi://%2fvar%2frun%2fldapi -b > > dc=ise,dc=luddy,dc=indiana,dc=edu '(objectClass=organizationalUnit)' > > dn: ou=people,dc=ise,dc=luddy,dc=indiana,dc=edu > > objectClass: organizationalUnit > > ou: people > > description: ISE Systems Users > > > > dn: ou=groups,dc=ise,dc=luddy,dc=indiana,dc=edu > > objectClass: organizationalUnit > > ou: groups > > description: ISE Systems Groups > > > > > > Note that these are missing the "description" attribute: > > > > $ ldap search -H ldapi://%2fvar%2frun%2fldapi -b > > dc=ise,dc=luddy,dc=indiana,dc=edu '(objectClass=organizationalUnit)' > > dn: ou=people,dc=ise,dc=luddy,dc=indiana,dc=edu > > objectClass: organizationalUnit > > ou: people > > > > dn: ou=groups,dc=ise,dc=luddy,dc=indiana,dc=edu > > objectClass: organizationalUnit > > ou: groups > > > > Allan > > > Thanks for the detailed report. > This edgecase got overlooked when moving to a stricter ASN.1 parsing for > ober_scanf_elements, which resulted in a premature exit of the loop. > Diff below should fix it. > > This diff also applies to libexec/login_ldap and usr.sbin/ypldap. > > OK? > > martijn@ Almost forgot about this one. Index: aldap.c === RCS file: /cvs/src/usr.bin/ldap/aldap.c,v retrieving revision 1.9 diff -u -p -r1.9 aldap.c --- aldap.c 24 Oct 2019 12:39:26 - 1.9 +++ aldap.c 8 Nov 2021 08:50:12 - @@ -580,15 +580,15 @@ int aldap_first_attr(struct aldap_message *msg, char **outkey, struct aldap_stringset **outvalues) { - struct ber_element *b, *c; + struct ber_element *b; char *key; struct aldap_stringset *ret; if (msg->body.search.attrs == NULL) goto fail; - if (ober_scanf_elements(msg->body.search.attrs, "{s(e)}e", - &key, &b, &c) != 0) + if (ober_scanf_elements(msg->body.search.attrs, "{s(e)}", + &key, &b) != 0) goto fail; msg->body.search.iter = msg->body.search.attrs->be_next; @@ -610,7 +610,7 @@ int aldap_next_attr(struct aldap_message *msg, char **outkey, struct aldap_stringset **outvalues) { - struct ber_element *a, *b; + struct ber_element *a; char *key; struct aldap_stringset *ret; @@ -622,8 +622,7 @@ aldap_next_attr(struct aldap_message *ms if (ober_get_eoc(msg->body.search.iter) == 0) goto notfound; - if (ober_scanf_elements(msg->body.search.iter, "{s(e)}e", &key, &a, &b) - != 0) + if (ober_scanf_elements(msg->body.search.iter, "{s(e)}", &key, &a) != 0) goto fail; msg->body.search.iter = msg->body.search.iter->be_next;