From f2ddd22eacdbc3262f55edab8aae5b6fcebd888c Mon Sep 17 00:00:00 2001 From: Gleb Smirnoff Date: Sun, 9 Sep 2012 09:46:48 +0000 Subject: [PATCH] For UDP transport set IP_RECVDSTADDR sockopt on the socket, and provide IP_SENDSRCADDR control with datagram message we reply with. This makes bsnmpd reply from exactly same address that request was sent to, thus successfully bypassing stateful firewalls or other kinds of strict checking. PR: bin/171279 --- contrib/bsnmp/snmpd/main.c | 39 ++++++++++++++++++++++++++++----- contrib/bsnmp/snmpd/trans_udp.c | 8 +++++++ 2 files changed, 41 insertions(+), 6 deletions(-) diff --git a/contrib/bsnmp/snmpd/main.c b/contrib/bsnmp/snmpd/main.c index 01a5818a0e8c..6fdc6d352ef6 100644 --- a/contrib/bsnmp/snmpd/main.c +++ b/contrib/bsnmp/snmpd/main.c @@ -1106,10 +1106,11 @@ recv_stream(struct port_input *pi) * Each receive should return one datagram. */ static int -recv_dgram(struct port_input *pi) +recv_dgram(struct port_input *pi, struct in_addr *laddr) { u_char embuf[1000]; - char cbuf[CMSG_SPACE(SOCKCREDSIZE(CMGROUP_MAX))]; + char cbuf[CMSG_SPACE(SOCKCREDSIZE(CMGROUP_MAX)) + + CMSG_SPACE(sizeof(struct in_addr))]; struct msghdr msg; struct iovec iov[1]; ssize_t len; @@ -1159,6 +1160,9 @@ recv_dgram(struct port_input *pi) for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) { + if (cmsg->cmsg_level == IPPROTO_IP && + cmsg->cmsg_type == IP_RECVDSTADDR) + memcpy(laddr, CMSG_DATA(cmsg), sizeof(struct in_addr)); if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_CREDS) cred = (struct sockcred *)CMSG_DATA(cmsg); @@ -1187,12 +1191,27 @@ snmpd_input(struct port_input *pi, struct tport *tport) #ifdef USE_TCPWRAPPERS char client[16]; #endif + struct msghdr msg; + struct iovec iov[1]; + char cbuf[CMSG_SPACE(sizeof(struct in_addr))]; + struct cmsghdr *cmsgp; /* get input depending on the transport */ if (pi->stream) { + msg.msg_control = NULL; + msg.msg_controllen = 0; + ret = recv_stream(pi); } else { - ret = recv_dgram(pi); + memset(cbuf, 0, CMSG_SPACE(sizeof(struct in_addr))); + msg.msg_control = cbuf; + msg.msg_controllen = CMSG_SPACE(sizeof(struct in_addr)); + cmsgp = CMSG_FIRSTHDR(&msg); + cmsgp->cmsg_len = CMSG_LEN(sizeof(struct in_addr)); + cmsgp->cmsg_level = IPPROTO_IP; + cmsgp->cmsg_type = IP_SENDSRCADDR; + + ret = recv_dgram(pi, (struct in_addr *)CMSG_DATA(cmsgp)); } if (ret == -1) @@ -1337,11 +1356,19 @@ snmpd_input(struct port_input *pi, struct tport *tport) sndbuf, &sndlen, "SNMP", ierr, vi, NULL); if (ferr == SNMPD_INPUT_OK) { - slen = sendto(pi->fd, sndbuf, sndlen, 0, pi->peer, pi->peerlen); + msg.msg_name = pi->peer; + msg.msg_namelen = pi->peerlen; + msg.msg_iov = iov; + msg.msg_iovlen = 1; + msg.msg_flags = 0; + iov[0].iov_base = sndbuf; + iov[0].iov_len = sndlen; + + slen = sendmsg(pi->fd, &msg, 0); if (slen == -1) - syslog(LOG_ERR, "sendto: %m"); + syslog(LOG_ERR, "sendmsg: %m"); else if ((size_t)slen != sndlen) - syslog(LOG_ERR, "sendto: short write %zu/%zu", + syslog(LOG_ERR, "sendmsg: short write %zu/%zu", sndlen, (size_t)slen); } snmp_pdu_free(&pdu); diff --git a/contrib/bsnmp/snmpd/trans_udp.c b/contrib/bsnmp/snmpd/trans_udp.c index acab70e314a4..0a119d2f297e 100644 --- a/contrib/bsnmp/snmpd/trans_udp.c +++ b/contrib/bsnmp/snmpd/trans_udp.c @@ -103,11 +103,19 @@ udp_init_port(struct tport *tp) struct udp_port *p = (struct udp_port *)tp; struct sockaddr_in addr; u_int32_t ip; + const int on = 1; if ((p->input.fd = socket(PF_INET, SOCK_DGRAM, 0)) < 0) { syslog(LOG_ERR, "creating UDP socket: %m"); return (SNMP_ERR_RES_UNAVAIL); } + if (setsockopt(p->input.fd, IPPROTO_IP, IP_RECVDSTADDR, &on, + sizeof(on)) == -1) { + syslog(LOG_ERR, "setsockopt(IP_RECVDSTADDR): %m"); + close(p->input.fd); + p->input.fd = -1; + return (SNMP_ERR_GENERR); + } ip = (p->addr[0] << 24) | (p->addr[1] << 16) | (p->addr[2] << 8) | p->addr[3]; memset(&addr, 0, sizeof(addr));