521175: [5.1] snmp communication using a broadcast address is unavailable. Source: upstream SVN rev. 17855 and 17908. UDP responses are sent with source IP address which was destination of appropriate requests (implemented in SVN rev. 15215). But if the destination of a request is broadcast IP address, the request was sent with the broadcast address as source. sendmsg() on Linux does not support this and returns error -> response is not sent. In order to send responses from the same interface, interface index of the appropriate interface must be used. diff -up net-snmp-5.3.2.2/snmplib/snmpUDPDomain.c.broadcast-response net-snmp-5.3.2.2/snmplib/snmpUDPDomain.c --- net-snmp-5.3.2.2/snmplib/snmpUDPDomain.c.broadcast-response 2009-11-30 17:21:28.000000000 +0100 +++ net-snmp-5.3.2.2/snmplib/snmpUDPDomain.c 2009-11-30 17:22:26.000000000 +0100 @@ -70,6 +70,7 @@ static netsnmp_tdomain udpDomain; typedef struct netsnmp_udp_addr_pair_s { struct sockaddr_in remote_addr; struct in_addr local_addr; + int if_index; } netsnmp_udp_addr_pair; /* @@ -121,7 +122,7 @@ netsnmp_udp_fmtaddr(netsnmp_transport *t # define netsnmp_dstaddr(x) (&(((struct in_pktinfo *)(CMSG_DATA(x)))->ipi_addr)) -static int netsnmp_udp_recvfrom(int s, char *buf, int len, struct sockaddr *from, int *fromlen, struct in_addr *dstip) +static int netsnmp_udp_recvfrom(int s, char *buf, int len, struct sockaddr *from, int *fromlen, struct in_addr *dstip, int *if_index) { int r; struct iovec iov[1]; @@ -150,41 +151,54 @@ static int netsnmp_udp_recvfrom(int s, c for (cmsgptr = CMSG_FIRSTHDR(&msg); cmsgptr != NULL; cmsgptr = CMSG_NXTHDR(&msg, cmsgptr)) { if (cmsgptr->cmsg_level == SOL_IP && cmsgptr->cmsg_type == IP_PKTINFO) { memcpy((void *) dstip, netsnmp_dstaddr(cmsgptr), sizeof(struct in_addr)); - DEBUGMSGTL(("netsnmp_udp", "got destination (local) addr %s\n", - inet_ntoa(*dstip))); + *if_index = (((struct in_pktinfo *)(CMSG_DATA(cmsgptr)))->ipi_ifindex); + DEBUGMSGTL(("netsnmp_udp", "got destination (local) addr %s, iface %d\n", + inet_ntoa(*dstip), *if_index)); } } return r; } -static int netsnmp_udp_sendto(int fd, struct in_addr *srcip, struct sockaddr *remote, - char *data, int len) +static int netsnmp_udp_sendto(int fd, struct in_addr *srcip, int if_index, + struct sockaddr *remote, char *data, int len) { struct iovec iov = { data, len }; struct { struct cmsghdr cm; struct in_pktinfo ipi; - } cmsg = { - .cm = { - .cmsg_len = sizeof(struct cmsghdr) + sizeof(struct in_pktinfo), - .cmsg_level = SOL_IP, - .cmsg_type = IP_PKTINFO, - }, - .ipi = { - .ipi_ifindex = 0, - .ipi_spec_dst = srcip ? srcip->s_addr : 0, - }, - }; - struct msghdr m = { - .msg_name = remote, - .msg_namelen = sizeof(struct sockaddr_in), - .msg_iov = &iov, - .msg_iovlen = 1, - .msg_control = &cmsg, - .msg_controllen = sizeof(cmsg), - .msg_flags = 0, - }; - return sendmsg(fd, &m, MSG_NOSIGNAL|MSG_DONTWAIT); + } cmsg; + struct msghdr m; + int ret; + + memset(&cmsg, 0, sizeof(cmsg)); + cmsg.cm.cmsg_len = sizeof(struct cmsghdr) + sizeof(struct in_pktinfo); + cmsg.cm.cmsg_level = SOL_IP; + cmsg.cm.cmsg_type = IP_PKTINFO; + cmsg.ipi.ipi_ifindex = if_index; + cmsg.ipi.ipi_spec_dst.s_addr = (srcip ? srcip->s_addr : INADDR_ANY); + + m.msg_name = remote; + m.msg_namelen = sizeof(struct sockaddr_in); + m.msg_iov = &iov; + m.msg_iovlen = 1; + m.msg_control = &cmsg; + m.msg_controllen= sizeof(cmsg); + m.msg_flags = 0; + + DEBUGMSGTL(("netsnmp_udp", "sending from %s iface %d\n", + (srcip ? inet_ntoa(*srcip) : "NULL"), if_index)); + errno = 0; + ret = sendmsg(fd, &m, MSG_NOSIGNAL|MSG_DONTWAIT); + if (ret < 0 && errno == EINVAL && srcip) { + /* The error might be caused by broadcast srcip (i.e. we're responding + * to broadcast request) - sendmsg does not like it. Try to resend it + * with global address. */ + cmsg.ipi.ipi_spec_dst.s_addr = INADDR_ANY; + DEBUGMSGTL(("netsnmp_udp", + "netsnmp_udp_sendto: re-sending the message\n")); + ret = sendmsg(fd, &m, MSG_NOSIGNAL|MSG_DONTWAIT); + } + return ret; } #endif /* IP_PKTINFO */ @@ -216,7 +230,8 @@ netsnmp_udp_recv(netsnmp_transport *t, v while (rc < 0) { #if defined IP_PKTINFO - rc = netsnmp_udp_recvfrom(t->sock, buf, size, from, &fromlen, &(addr_pair->local_addr)); + rc = netsnmp_udp_recvfrom(t->sock, buf, size, from, &fromlen, + &(addr_pair->local_addr), &(addr_pair->if_index)); #else rc = recvfrom(t->sock, buf, size, 0, from, &fromlen); #endif /* IP_PKTINFO */ @@ -269,7 +284,10 @@ netsnmp_udp_send(netsnmp_transport *t, v free(str); while (rc < 0) { #if defined IP_PKTINFO - rc = netsnmp_udp_sendto(t->sock, addr_pair ? &(addr_pair->local_addr) : NULL, to, buf, size); + rc = netsnmp_udp_sendto(t->sock, + addr_pair ? &(addr_pair->local_addr) : NULL, + addr_pair ? addr_pair->if_index : 0, + to, buf, size); #else rc = sendto(t->sock, buf, size, 0, to, sizeof(struct sockaddr)); #endif /* IP_PKTINFO */ diff -up net-snmp-5.3.2.2/snmplib/snmpTCPDomain.c.broadcast-response net-snmp-5.3.2.2/snmplib/snmpTCPDomain.c --- net-snmp-5.3.2.2/snmplib/snmpTCPDomain.c.broadcast-response 2009-12-14 14:42:30.000000000 +0100 +++ net-snmp-5.3.2.2/snmplib/snmpTCPDomain.c 2009-12-14 14:42:49.000000000 +0100 @@ -48,6 +48,7 @@ typedef struct netsnmp_udp_addr_pair_s { struct sockaddr_in remote_addr; struct in_addr local_addr; + int if_index; } netsnmp_udp_addr_pair; oid netsnmp_snmpTCPDomain[] = { TRANSPORT_DOMAIN_TCP_IP };