Re: snmpd source address

2016-11-02 Thread Jeremie Courreges-Anglas
Jeremie Courreges-Anglas  writes:

> Jeremie Courreges-Anglas  writes:
>
>> Jeremie Courreges-Anglas  writes:
>>
>>> Jeremie Courreges-Anglas  writes:
>>>
 j...@wxcvbn.org (Jeremie Courreges-Anglas) writes:

> SNMP uses UDP and snmpd listens on a single address.  This means that
> you can have issues with the source address of the replies sent by
> snmpd.  "listen on $loopback_address":
>
>   http://marc.info/?l=openbsd-misc=147445870822415=2
>
> seems to be unsufficient, but I don't understand why it would fail.
>
> The diff below should fix the source address selection for replies.
> Configuring the source address for traps is a different problem and will
> be implemented in a different diff.

 Updated diff that implements a "source-address" parameter for trap
 receivers, as discussed with Reyk.

> WIP, only tested on a single box so far.  Thoughts / oks?

 source-address lightly tested with:
 trap receiver 127.0.0.1
 trap receiver 127.0.0.1 source-address 192.168.0.44
 trap receiver fe80::1%lo0 source-address ::1

 Comments and test reports welcome.
>>>
>>> Notes regarding source-address:
>>> - only try to handle an address, not a hostname.  I didn't want
>>>   to handle the additional complexity as I'm not sure it's worth it.
>>> - say we have "trap receiver somehostname source-address fd00::1".
>>>   Should we try to filter out potential IPv4 addresses and find an
>>>   appropriate IPv6 address?  The diff below would error out if the first
>>>   record found is an IPv4.
>>
>> I decided to go ahead and skip records in case of AF mismatch.  Lightly
>> tested with:
>>  - /etc/snmpd.conf
>>   trap receiver somehostname source-address ::1
>>  - $ ifconfig lo1
>>   lo1: flags=8049 mtu 32768
>> index 6 priority 0 llprio 3
>> groups: lo
>> inet 192.0.2.1 netmask 0xff00
>> inet6 ::1 prefixlen 128
>> inet6 fe80::1%lo1 prefixlen 64 scopeid 0x6
>> inet6 2001:db8::1 prefixlen 64
>>  - /etc/hosts
>>   192.0.2.1   somehostname
>>   2001:db8::1 somehostname
>>
>> Comments and test reports still welcome. :)
>
> Ping.  Now with manpage bits.

Updated diff after recent commits in snmpd.


Index: parse.y
===
RCS file: /d/cvs/src/usr.sbin/snmpd/parse.y,v
retrieving revision 1.39
diff -u -p -r1.39 parse.y
--- parse.y 21 Jun 2016 21:35:25 -  1.39
+++ parse.y 1 Nov 2016 17:45:40 -
@@ -98,9 +98,10 @@ static intnctlsocks = 0;
 struct address *host_v4(const char *);
 struct address *host_v6(const char *);
 int host_dns(const char *, struct addresslist *,
-   int, in_port_t, struct ber_oid *, char *);
+   int, in_port_t, struct ber_oid *, char *,
+   struct address *);
 int host(const char *, struct addresslist *,
-   int, in_port_t, struct ber_oid *, char *);
+   int, in_port_t, struct ber_oid *, char *, char *);
 
 typedef struct {
union {
@@ -127,10 +128,11 @@ 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 DISABLED
-%token SOCKET RESTRICTED AGENTX HANDLE DEFAULT
+%token SOCKET RESTRICTED AGENTX HANDLE DEFAULT SRCADDR
 %token   STRING
 %token   NUMBER
 %typehostcmn
+%typesrcaddr
 %typeoptwrite yesno seclevel socktype
 %type  objtype cmd
 %type   oid hostoid trapoid
@@ -200,7 +202,8 @@ main: LISTEN ON STRING  {
struct address  *h;
 
TAILQ_INIT();
-   if (host($3, , 1, SNMPD_PORT, NULL, NULL) <= 0) {
+   if (host($3, , 1, SNMPD_PORT, NULL, NULL, NULL)
+   <= 0) {
yyerror("invalid ip address: %s", $3);
free($3);
YYERROR;
@@ -445,9 +448,13 @@ hostcmn: /* empty */   
{ $$ = NULL; }
| COMMUNITY STRING  { $$ = $2; }
;
 
-hostdef: STRING hostoid hostcmn{
+srcaddr: /* empty */   { $$ = NULL; }
+   | SRCADDR STRING{ $$ = $2; }
+   ;
+
+hostdef: STRING hostoid hostcmn srcaddr{
if (host($1, hlist, 1,
-   SNMPD_TRAPPORT, $2, $3) <= 0) {
+   SNMPD_TRAPPORT, $2, $3, $4) <= 0) {
yyerror("invalid 

Re: snmpd source address

2016-10-21 Thread Jeremie Courreges-Anglas
Jeremie Courreges-Anglas  writes:

> Jeremie Courreges-Anglas  writes:
>
>> Jeremie Courreges-Anglas  writes:
>>
>>> j...@wxcvbn.org (Jeremie Courreges-Anglas) writes:
>>>
 SNMP uses UDP and snmpd listens on a single address.  This means that
 you can have issues with the source address of the replies sent by
 snmpd.  "listen on $loopback_address":

   http://marc.info/?l=openbsd-misc=147445870822415=2

 seems to be unsufficient, but I don't understand why it would fail.

 The diff below should fix the source address selection for replies.
 Configuring the source address for traps is a different problem and will
 be implemented in a different diff.
>>>
>>> Updated diff that implements a "source-address" parameter for trap
>>> receivers, as discussed with Reyk.
>>>
 WIP, only tested on a single box so far.  Thoughts / oks?
>>>
>>> source-address lightly tested with:
>>> trap receiver 127.0.0.1
>>> trap receiver 127.0.0.1 source-address 192.168.0.44
>>> trap receiver fe80::1%lo0 source-address ::1
>>>
>>> Comments and test reports welcome.
>>
>> Notes regarding source-address:
>> - only try to handle an address, not a hostname.  I didn't want
>>   to handle the additional complexity as I'm not sure it's worth it.
>> - say we have "trap receiver somehostname source-address fd00::1".
>>   Should we try to filter out potential IPv4 addresses and find an
>>   appropriate IPv6 address?  The diff below would error out if the first
>>   record found is an IPv4.
>
> I decided to go ahead and skip records in case of AF mismatch.  Lightly
> tested with:
>  - /etc/snmpd.conf
>   trap receiver somehostname source-address ::1
>  - $ ifconfig lo1
>   lo1: flags=8049 mtu 32768
> index 6 priority 0 llprio 3
> groups: lo
> inet 192.0.2.1 netmask 0xff00
> inet6 ::1 prefixlen 128
> inet6 fe80::1%lo1 prefixlen 64 scopeid 0x6
> inet6 2001:db8::1 prefixlen 64
>  - /etc/hosts
>   192.0.2.1   somehostname
>   2001:db8::1 somehostname
>
> Comments and test reports still welcome. :)

Ping.  Now with manpage bits.


Index: parse.y
===
RCS file: /d/cvs/src/usr.sbin/snmpd/parse.y,v
retrieving revision 1.39
diff -u -p -r1.39 parse.y
--- parse.y 21 Jun 2016 21:35:25 -  1.39
+++ parse.y 21 Oct 2016 11:28:15 -
@@ -98,9 +98,10 @@ static intnctlsocks = 0;
 struct address *host_v4(const char *);
 struct address *host_v6(const char *);
 int host_dns(const char *, struct addresslist *,
-   int, in_port_t, struct ber_oid *, char *);
+   int, in_port_t, struct ber_oid *, char *,
+   struct address *);
 int host(const char *, struct addresslist *,
-   int, in_port_t, struct ber_oid *, char *);
+   int, in_port_t, struct ber_oid *, char *, char *);
 
 typedef struct {
union {
@@ -127,10 +128,11 @@ 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 DISABLED
-%token SOCKET RESTRICTED AGENTX HANDLE DEFAULT
+%token SOCKET RESTRICTED AGENTX HANDLE DEFAULT SRCADDR
 %token   STRING
 %token   NUMBER
 %typehostcmn
+%typesrcaddr
 %typeoptwrite yesno seclevel socktype
 %type  objtype cmd
 %type   oid hostoid trapoid
@@ -200,7 +202,8 @@ main: LISTEN ON STRING  {
struct address  *h;
 
TAILQ_INIT();
-   if (host($3, , 1, SNMPD_PORT, NULL, NULL) <= 0) {
+   if (host($3, , 1, SNMPD_PORT, NULL, NULL, NULL)
+   <= 0) {
yyerror("invalid ip address: %s", $3);
free($3);
YYERROR;
@@ -445,9 +448,13 @@ hostcmn: /* empty */   
{ $$ = NULL; }
| COMMUNITY STRING  { $$ = $2; }
;
 
-hostdef: STRING hostoid hostcmn{
+srcaddr: /* empty */   { $$ = NULL; }
+   | SRCADDR STRING{ $$ = $2; }
+   ;
+
+hostdef: STRING hostoid hostcmn srcaddr{
if (host($1, hlist, 1,
-   SNMPD_TRAPPORT, $2, $3) <= 0) {
+   SNMPD_TRAPPORT, $2, $3, $4) <= 0) {
yyerror("invalid host: %s", $1);
free($1);
YYERROR;
@@ -636,6 +643,7 @@ lookup(char *s)
{ 

Re: snmpd source address

2016-10-07 Thread Jeremie Courreges-Anglas
Jeremie Courreges-Anglas  writes:

> Jeremie Courreges-Anglas  writes:
>
>> j...@wxcvbn.org (Jeremie Courreges-Anglas) writes:
>>
>>> SNMP uses UDP and snmpd listens on a single address.  This means that
>>> you can have issues with the source address of the replies sent by
>>> snmpd.  "listen on $loopback_address":
>>>
>>>   http://marc.info/?l=openbsd-misc=147445870822415=2
>>>
>>> seems to be unsufficient, but I don't understand why it would fail.
>>>
>>> The diff below should fix the source address selection for replies.
>>> Configuring the source address for traps is a different problem and will
>>> be implemented in a different diff.
>>
>> Updated diff that implements a "source-address" parameter for trap
>> receivers, as discussed with Reyk.
>>
>>> WIP, only tested on a single box so far.  Thoughts / oks?
>>
>> source-address lightly tested with:
>> trap receiver 127.0.0.1
>> trap receiver 127.0.0.1 source-address 192.168.0.44
>> trap receiver fe80::1%lo0 source-address ::1
>>
>> Comments and test reports welcome.
>
> Notes regarding source-address:
> - only try to handle an address, not a hostname.  I didn't want
>   to handle the additional complexity as I'm not sure it's worth it.
> - say we have "trap receiver somehostname source-address fd00::1".
>   Should we try to filter out potential IPv4 addresses and find an
>   appropriate IPv6 address?  The diff below would error out if the first
>   record found is an IPv4.

I decided to go ahead and skip records in case of AF mismatch.  Lightly
tested with:
 - /etc/snmpd.conf
  trap receiver somehostname source-address ::1
 - $ ifconfig lo1
  lo1: flags=8049 mtu 32768
index 6 priority 0 llprio 3
groups: lo
inet 192.0.2.1 netmask 0xff00
inet6 ::1 prefixlen 128
inet6 fe80::1%lo1 prefixlen 64 scopeid 0x6
inet6 2001:db8::1 prefixlen 64
 - /etc/hosts
  192.0.2.1   somehostname
  2001:db8::1 somehostname

Comments and test reports still welcome. :)


Index: parse.y
===
RCS file: /cvs/src/usr.sbin/snmpd/parse.y,v
retrieving revision 1.39
diff -u -p -r1.39 parse.y
--- parse.y 21 Jun 2016 21:35:25 -  1.39
+++ parse.y 30 Sep 2016 14:15:25 -
@@ -98,9 +98,10 @@ static intnctlsocks = 0;
 struct address *host_v4(const char *);
 struct address *host_v6(const char *);
 int host_dns(const char *, struct addresslist *,
-   int, in_port_t, struct ber_oid *, char *);
+   int, in_port_t, struct ber_oid *, char *,
+   struct address *);
 int host(const char *, struct addresslist *,
-   int, in_port_t, struct ber_oid *, char *);
+   int, in_port_t, struct ber_oid *, char *, char *);
 
 typedef struct {
union {
@@ -127,10 +128,11 @@ 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 DISABLED
-%token SOCKET RESTRICTED AGENTX HANDLE DEFAULT
+%token SOCKET RESTRICTED AGENTX HANDLE DEFAULT SRCADDR
 %token   STRING
 %token   NUMBER
 %typehostcmn
+%typesrcaddr
 %typeoptwrite yesno seclevel socktype
 %type  objtype cmd
 %type   oid hostoid trapoid
@@ -200,7 +202,8 @@ main: LISTEN ON STRING  {
struct address  *h;
 
TAILQ_INIT();
-   if (host($3, , 1, SNMPD_PORT, NULL, NULL) <= 0) {
+   if (host($3, , 1, SNMPD_PORT, NULL, NULL, NULL)
+   <= 0) {
yyerror("invalid ip address: %s", $3);
free($3);
YYERROR;
@@ -445,9 +448,13 @@ hostcmn: /* empty */   
{ $$ = NULL; }
| COMMUNITY STRING  { $$ = $2; }
;
 
-hostdef: STRING hostoid hostcmn{
+srcaddr: /* empty */   { $$ = NULL; }
+   | SRCADDR STRING{ $$ = $2; }
+   ;
+
+hostdef: STRING hostoid hostcmn srcaddr{
if (host($1, hlist, 1,
-   SNMPD_TRAPPORT, $2, $3) <= 0) {
+   SNMPD_TRAPPORT, $2, $3, $4) <= 0) {
yyerror("invalid host: %s", $1);
free($1);
YYERROR;
@@ -636,6 +643,7 @@ lookup(char *s)
{ "seclevel",   SECLEVEL },
{ "services",   SERVICES },
{ "socket", SOCKET },
+   { 

Re: snmpd source address

2016-09-30 Thread Jeremie Courreges-Anglas
Jeremie Courreges-Anglas  writes:

> j...@wxcvbn.org (Jeremie Courreges-Anglas) writes:
>
>> SNMP uses UDP and snmpd listens on a single address.  This means that
>> you can have issues with the source address of the replies sent by
>> snmpd.  "listen on $loopback_address":
>>
>>   http://marc.info/?l=openbsd-misc=147445870822415=2
>>
>> seems to be unsufficient, but I don't understand why it would fail.
>>
>> The diff below should fix the source address selection for replies.
>> Configuring the source address for traps is a different problem and will
>> be implemented in a different diff.
>
> Updated diff that implements a "source-address" parameter for trap
> receivers, as discussed with Reyk.
>
>> WIP, only tested on a single box so far.  Thoughts / oks?
>
> source-address lightly tested with:
> trap receiver 127.0.0.1
> trap receiver 127.0.0.1 source-address 192.168.0.44
> trap receiver fe80::1%lo0 source-address ::1
>
> Comments and test reports welcome.

Notes regarding source-address:
- only try to handle an address, not a hostname.  I didn't want
  to handle the additional complexity as I'm not sure it's worth it.
- say we have "trap receiver somehostname source-address fd00::1".
  Should we try to filter out potential IPv4 addresses and find an
  appropriate IPv6 address?  The diff below would error out if the first
  record found is an IPv4.

>
> Index: parse.y
> ===
> RCS file: /cvs/src/usr.sbin/snmpd/parse.y,v
> retrieving revision 1.39
> diff -u -p -r1.39 parse.y
> --- parse.y   21 Jun 2016 21:35:25 -  1.39
> +++ parse.y   30 Sep 2016 13:33:28 -
> @@ -98,9 +98,10 @@ static int  nctlsocks = 0;
>  struct address   *host_v4(const char *);
>  struct address   *host_v6(const char *);
>  int   host_dns(const char *, struct addresslist *,
> - int, in_port_t, struct ber_oid *, char *);
> + int, in_port_t, struct ber_oid *, char *,
> + struct address *);
>  int   host(const char *, struct addresslist *,
> - int, in_port_t, struct ber_oid *, char *);
> + int, in_port_t, struct ber_oid *, char *, char *);
>  
>  typedef struct {
>   union {
> @@ -127,10 +128,11 @@ 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 DISABLED
> -%token   SOCKET RESTRICTED AGENTX HANDLE DEFAULT
> +%token   SOCKET RESTRICTED AGENTX HANDLE DEFAULT SRCADDR
>  %token STRING
>  %token NUMBER
>  %type  hostcmn
> +%type  srcaddr
>  %type  optwrite yesno seclevel socktype
>  %typeobjtype cmd
>  %type oid hostoid trapoid
> @@ -200,7 +202,8 @@ main  : LISTEN ON STRING  {
>   struct address  *h;
>  
>   TAILQ_INIT();
> - if (host($3, , 1, SNMPD_PORT, NULL, NULL) <= 0) {
> + if (host($3, , 1, SNMPD_PORT, NULL, NULL, NULL)
> + <= 0) {
>   yyerror("invalid ip address: %s", $3);
>   free($3);
>   YYERROR;
> @@ -445,9 +448,13 @@ hostcmn  : /* empty */   
> { $$ = NULL; }
>   | COMMUNITY STRING  { $$ = $2; }
>   ;
>  
> -hostdef  : STRING hostoid hostcmn{
> +srcaddr  : /* empty */   { $$ = NULL; }
> + | SRCADDR STRING{ $$ = $2; }
> + ;
> +
> +hostdef  : STRING hostoid hostcmn srcaddr{
>   if (host($1, hlist, 1,
> - SNMPD_TRAPPORT, $2, $3) <= 0) {
> + SNMPD_TRAPPORT, $2, $3, $4) <= 0) {
>   yyerror("invalid host: %s", $1);
>   free($1);
>   YYERROR;
> @@ -636,6 +643,7 @@ lookup(char *s)
>   { "seclevel",   SECLEVEL },
>   { "services",   SERVICES },
>   { "socket", SOCKET },
> + { "source-address", SRCADDR },
>   { "string", OCTETSTRING },
>   { "system", SYSTEM },
>   { "trap",   TRAP },
> @@ -1151,7 +1159,7 @@ host_v6(const char *s)
>  
>  int
>  host_dns(const char *s, struct addresslist *al, int max,
> -  in_port_t port, struct ber_oid *oid, char *cmn)
> + in_port_t port, struct ber_oid *oid, char *cmn, struct address *src)
>  {
>   struct addrinfo  hints, *res0, *res;
>   int  error, cnt 

Re: snmpd source address

2016-09-30 Thread Jeremie Courreges-Anglas
j...@wxcvbn.org (Jeremie Courreges-Anglas) writes:

> SNMP uses UDP and snmpd listens on a single address.  This means that
> you can have issues with the source address of the replies sent by
> snmpd.  "listen on $loopback_address":
>
>   http://marc.info/?l=openbsd-misc=147445870822415=2
>
> seems to be unsufficient, but I don't understand why it would fail.
>
> The diff below should fix the source address selection for replies.
> Configuring the source address for traps is a different problem and will
> be implemented in a different diff.

Updated diff that implements a "source-address" parameter for trap
receivers, as discussed with Reyk.

> WIP, only tested on a single box so far.  Thoughts / oks?

source-address lightly tested with:
trap receiver 127.0.0.1
trap receiver 127.0.0.1 source-address 192.168.0.44
trap receiver fe80::1%lo0 source-address ::1

Comments and test reports welcome.


Index: parse.y
===
RCS file: /cvs/src/usr.sbin/snmpd/parse.y,v
retrieving revision 1.39
diff -u -p -r1.39 parse.y
--- parse.y 21 Jun 2016 21:35:25 -  1.39
+++ parse.y 30 Sep 2016 13:33:28 -
@@ -98,9 +98,10 @@ static intnctlsocks = 0;
 struct address *host_v4(const char *);
 struct address *host_v6(const char *);
 int host_dns(const char *, struct addresslist *,
-   int, in_port_t, struct ber_oid *, char *);
+   int, in_port_t, struct ber_oid *, char *,
+   struct address *);
 int host(const char *, struct addresslist *,
-   int, in_port_t, struct ber_oid *, char *);
+   int, in_port_t, struct ber_oid *, char *, char *);
 
 typedef struct {
union {
@@ -127,10 +128,11 @@ 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 DISABLED
-%token SOCKET RESTRICTED AGENTX HANDLE DEFAULT
+%token SOCKET RESTRICTED AGENTX HANDLE DEFAULT SRCADDR
 %token   STRING
 %token   NUMBER
 %typehostcmn
+%typesrcaddr
 %typeoptwrite yesno seclevel socktype
 %type  objtype cmd
 %type   oid hostoid trapoid
@@ -200,7 +202,8 @@ main: LISTEN ON STRING  {
struct address  *h;
 
TAILQ_INIT();
-   if (host($3, , 1, SNMPD_PORT, NULL, NULL) <= 0) {
+   if (host($3, , 1, SNMPD_PORT, NULL, NULL, NULL)
+   <= 0) {
yyerror("invalid ip address: %s", $3);
free($3);
YYERROR;
@@ -445,9 +448,13 @@ hostcmn: /* empty */   
{ $$ = NULL; }
| COMMUNITY STRING  { $$ = $2; }
;
 
-hostdef: STRING hostoid hostcmn{
+srcaddr: /* empty */   { $$ = NULL; }
+   | SRCADDR STRING{ $$ = $2; }
+   ;
+
+hostdef: STRING hostoid hostcmn srcaddr{
if (host($1, hlist, 1,
-   SNMPD_TRAPPORT, $2, $3) <= 0) {
+   SNMPD_TRAPPORT, $2, $3, $4) <= 0) {
yyerror("invalid host: %s", $1);
free($1);
YYERROR;
@@ -636,6 +643,7 @@ lookup(char *s)
{ "seclevel",   SECLEVEL },
{ "services",   SERVICES },
{ "socket", SOCKET },
+   { "source-address", SRCADDR },
{ "string", OCTETSTRING },
{ "system", SYSTEM },
{ "trap",   TRAP },
@@ -1151,7 +1159,7 @@ host_v6(const char *s)
 
 int
 host_dns(const char *s, struct addresslist *al, int max,
-in_port_t port, struct ber_oid *oid, char *cmn)
+   in_port_t port, struct ber_oid *oid, char *cmn, struct address *src)
 {
struct addrinfo  hints, *res0, *res;
int  error, cnt = 0;
@@ -1202,6 +1210,13 @@ host_dns(const char *s, struct addressli
memcpy(>sin6_addr, &((struct sockaddr_in6 *)
res->ai_addr)->sin6_addr, sizeof(struct in6_addr));
}
+   if (src != NULL) {
+   if (h->ss.ss_family != src->ss.ss_family) {
+   log_warnx("host and source-address family 
mismatch");
+   return (-1);
+   }
+   h->sa_srcaddr = src;
+   }
 
TAILQ_INSERT_HEAD(al, h, entry);

snmpd source address

2016-09-28 Thread Jeremie Courreges-Anglas

SNMP uses UDP and snmpd listens on a single address.  This means that
you can have issues with the source address of the replies sent by
snmpd.  "listen on $loopback_address":

  http://marc.info/?l=openbsd-misc=147445870822415=2

seems to be unsufficient, but I don't understand why it would fail.

The diff below should fix the source address selection for replies.
Configuring the source address for traps is a different problem and will
be implemented in a different diff.

WIP, only tested on a single box so far.  Thoughts / oks?


Index: snmpd.h
===
RCS file: /cvs/src/usr.sbin/snmpd/snmpd.h,v
retrieving revision 1.68
diff -u -p -r1.68 snmpd.h
--- snmpd.h 25 Sep 2016 14:58:00 -  1.68
+++ snmpd.h 28 Sep 2016 20:11:05 -
@@ -390,6 +390,9 @@ struct snmp_message {
socklen_tsm_slen;
char sm_host[HOST_NAME_MAX+1];
 
+   struct sockaddr_storage  sm_local_ss;
+   socklen_tsm_local_slen;
+
struct ber   sm_ber;
struct ber_element  *sm_req;
struct ber_element  *sm_resp;
@@ -766,6 +769,10 @@ struct trapcmd *
 /* util.c */
 int varbind_convert(struct agentx_pdu *, struct agentx_varbind_hdr *,
struct ber_element **, struct ber_element **);
+ssize_t sendtofrom(int, void *, size_t, int, struct sockaddr *,
+   socklen_t, struct sockaddr *, socklen_t);
+ssize_t recvfromto(int, void *, size_t, int, struct sockaddr *,
+   socklen_t *, struct sockaddr *, socklen_t *);
 voidprint_debug(const char *, ...);
 voidprint_verbose(const char *, ...);
 const char *log_in6addr(const struct in6_addr *);
Index: snmpe.c
===
RCS file: /cvs/src/usr.sbin/snmpd/snmpe.c,v
retrieving revision 1.42
diff -u -p -r1.42 snmpe.c
--- snmpe.c 16 Aug 2016 18:41:57 -  1.42
+++ snmpe.c 28 Sep 2016 20:11:05 -
@@ -120,7 +120,7 @@ int
 snmpe_bind(struct address *addr)
 {
char buf[512];
-   int  s;
+   int  val, s;
 
if ((s = snmpd_socket_af(>ss, htons(addr->port))) == -1)
return (-1);
@@ -131,6 +131,26 @@ snmpe_bind(struct address *addr)
if (fcntl(s, F_SETFL, O_NONBLOCK) == -1)
goto bad;
 
+   switch (addr->ss.ss_family) {
+   case AF_INET:
+   val = 1;
+   if (setsockopt(s, IPPROTO_IP, IP_RECVDSTADDR,
+   , sizeof(int)) == -1) {
+   log_warn("%s: failed to set IPv4 packet info",
+   __func__);
+   goto bad;
+   }
+   break;
+   case AF_INET6:
+   val = 1;
+   if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVPKTINFO,
+   , sizeof(int)) == -1) {
+   log_warn("%s: failed to set IPv6 packet info",
+   __func__);
+   goto bad;
+   }
+   }
+
if (bind(s, (struct sockaddr *)>ss, addr->ss.ss_len) == -1)
goto bad;
 
@@ -460,8 +480,9 @@ snmpe_recvmsg(int fd, short sig, void *a
return;
 
msg->sm_slen = sizeof(msg->sm_ss);
-   if ((len = recvfrom(fd, msg->sm_data, sizeof(msg->sm_data), 0,
-   (struct sockaddr *)>sm_ss, >sm_slen)) < 1) {
+   if ((len = recvfromto(fd, msg->sm_data, sizeof(msg->sm_data), 0,
+   (struct sockaddr *)>sm_ss, >sm_slen,
+   (struct sockaddr *)>sm_local_ss, >sm_local_slen)) < 1) {
free(msg);
return;
}
@@ -549,8 +570,9 @@ snmpe_response(int fd, struct snmp_messa
goto done;
 
usm_finalize_digest(msg, ptr, len);
-   len = sendto(fd, ptr, len, 0, (struct sockaddr *)>sm_ss,
-   msg->sm_slen);
+   len = sendtofrom(fd, ptr, len, 0,
+   (struct sockaddr *)>sm_ss, msg->sm_slen,
+   (struct sockaddr *)>sm_local_ss, msg->sm_local_slen);
if (len != -1)
stats->snmp_outpkts++;
 
Index: util.c
===
RCS file: /cvs/src/usr.sbin/snmpd/util.c,v
retrieving revision 1.5
diff -u -p -r1.5 util.c
--- util.c  21 Nov 2015 13:06:22 -  1.5
+++ util.c  28 Sep 2016 20:11:05 -
@@ -156,6 +156,128 @@ varbind_convert(struct agentx_pdu *pdu, 
return (ret);
 }
 
+ssize_t
+sendtofrom(int s, void *buf, size_t len, int flags, struct sockaddr *to,
+socklen_t tolen, struct sockaddr *from, socklen_t fromlen)
+{
+   struct iovec iov;
+   struct msghdrmsg;
+   struct cmsghdr  *cmsg;
+   struct in6_pktinfo  *pkt6;
+   struct sockaddr_in  *in;
+   struct sockaddr_in6 *in6;
+   union {
+   struct cmsghdr  hdr;
+   char