trans_udp.c revision 312267
1124861Sharti/* 2124861Sharti * Copyright (c) 2003 3124861Sharti * Fraunhofer Institute for Open Communication Systems (FhG Fokus). 4124861Sharti * All rights reserved. 5124861Sharti * 6124861Sharti * Author: Harti Brandt <harti@freebsd.org> 7310901Sngie * 8133211Sharti * Redistribution and use in source and binary forms, with or without 9133211Sharti * modification, are permitted provided that the following conditions 10133211Sharti * are met: 11133211Sharti * 1. Redistributions of source code must retain the above copyright 12133211Sharti * notice, this list of conditions and the following disclaimer. 13124861Sharti * 2. Redistributions in binary form must reproduce the above copyright 14124861Sharti * notice, this list of conditions and the following disclaimer in the 15124861Sharti * documentation and/or other materials provided with the distribution. 16310901Sngie * 17133211Sharti * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18133211Sharti * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19133211Sharti * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20133211Sharti * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 21133211Sharti * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22133211Sharti * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23133211Sharti * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24133211Sharti * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25133211Sharti * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26133211Sharti * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27133211Sharti * SUCH DAMAGE. 28124861Sharti * 29150920Sharti * $Begemot: bsnmp/snmpd/trans_udp.c,v 1.5 2005/10/04 08:46:56 brandt_h Exp $ 30124861Sharti * 31124861Sharti * UDP transport 32124861Sharti */ 33124861Sharti#include <sys/types.h> 34216294Ssyrinx#include <sys/queue.h> 35312057Sngie#include <sys/ucred.h> 36124861Sharti 37124861Sharti#include <stdlib.h> 38124861Sharti#include <syslog.h> 39124861Sharti#include <string.h> 40124861Sharti#include <errno.h> 41124861Sharti#include <unistd.h> 42124861Sharti 43124861Sharti#include <netinet/in.h> 44124861Sharti#include <arpa/inet.h> 45124861Sharti 46124861Sharti#include "snmpmod.h" 47124861Sharti#include "snmpd.h" 48124861Sharti#include "trans_udp.h" 49124861Sharti#include "tree.h" 50124861Sharti#include "oid.h" 51124861Sharti 52124861Shartistatic int udp_start(void); 53124861Shartistatic int udp_stop(int); 54124861Shartistatic void udp_close_port(struct tport *); 55124861Shartistatic int udp_init_port(struct tport *); 56124861Shartistatic ssize_t udp_send(struct tport *, const u_char *, size_t, 57124861Sharti const struct sockaddr *, size_t); 58312264Sngiestatic ssize_t udp_recv(struct tport *, struct port_input *); 59124861Sharti 60124861Sharti/* exported */ 61124861Sharticonst struct transport_def udp_trans = { 62124861Sharti "udp", 63124861Sharti OIDX_begemotSnmpdTransUdp, 64124861Sharti udp_start, 65124861Sharti udp_stop, 66124861Sharti udp_close_port, 67124861Sharti udp_init_port, 68312057Sngie udp_send, 69312057Sngie udp_recv 70124861Sharti}; 71124861Shartistatic struct transport *my_trans; 72124861Sharti 73124861Shartistatic int 74124861Shartiudp_start(void) 75124861Sharti{ 76124861Sharti return (trans_register(&udp_trans, &my_trans)); 77124861Sharti} 78124861Sharti 79124861Shartistatic int 80124861Shartiudp_stop(int force __unused) 81124861Sharti{ 82124861Sharti if (my_trans != NULL) 83124861Sharti if (trans_unregister(my_trans) != 0) 84124861Sharti return (SNMP_ERR_GENERR); 85124861Sharti return (SNMP_ERR_NOERROR); 86124861Sharti} 87124861Sharti 88124861Sharti/* 89124861Sharti * A UDP port is ready 90124861Sharti */ 91124861Shartistatic void 92124861Shartiudp_input(int fd __unused, void *udata) 93124861Sharti{ 94124861Sharti struct udp_port *p = udata; 95124861Sharti 96124861Sharti p->input.peerlen = sizeof(p->ret); 97124861Sharti snmpd_input(&p->input, &p->tport); 98124861Sharti} 99124861Sharti 100124861Sharti/* 101124861Sharti * Create a UDP socket and bind it to the given port 102124861Sharti */ 103124861Shartistatic int 104124861Shartiudp_init_port(struct tport *tp) 105124861Sharti{ 106124861Sharti struct udp_port *p = (struct udp_port *)tp; 107124861Sharti struct sockaddr_in addr; 108124861Sharti u_int32_t ip; 109240271Sglebius const int on = 1; 110124861Sharti 111124861Sharti if ((p->input.fd = socket(PF_INET, SOCK_DGRAM, 0)) < 0) { 112124861Sharti syslog(LOG_ERR, "creating UDP socket: %m"); 113124861Sharti return (SNMP_ERR_RES_UNAVAIL); 114124861Sharti } 115124861Sharti ip = (p->addr[0] << 24) | (p->addr[1] << 16) | (p->addr[2] << 8) | 116124861Sharti p->addr[3]; 117124861Sharti memset(&addr, 0, sizeof(addr)); 118124861Sharti addr.sin_addr.s_addr = htonl(ip); 119124861Sharti addr.sin_port = htons(p->port); 120124861Sharti addr.sin_family = AF_INET; 121124861Sharti addr.sin_len = sizeof(addr); 122240734Sglebius if (addr.sin_addr.s_addr == INADDR_ANY && 123240734Sglebius setsockopt(p->input.fd, IPPROTO_IP, IP_RECVDSTADDR, &on, 124240734Sglebius sizeof(on)) == -1) { 125240734Sglebius syslog(LOG_ERR, "setsockopt(IP_RECVDSTADDR): %m"); 126240734Sglebius close(p->input.fd); 127240734Sglebius p->input.fd = -1; 128240734Sglebius return (SNMP_ERR_GENERR); 129240734Sglebius } 130124861Sharti if (bind(p->input.fd, (struct sockaddr *)&addr, sizeof(addr))) { 131124861Sharti if (errno == EADDRNOTAVAIL) { 132124861Sharti close(p->input.fd); 133124861Sharti p->input.fd = -1; 134124861Sharti return (SNMP_ERR_INCONS_NAME); 135124861Sharti } 136124861Sharti syslog(LOG_ERR, "bind: %s:%u %m", inet_ntoa(addr.sin_addr), 137124861Sharti p->port); 138124861Sharti close(p->input.fd); 139124861Sharti p->input.fd = -1; 140124861Sharti return (SNMP_ERR_GENERR); 141124861Sharti } 142124861Sharti if ((p->input.id = fd_select(p->input.fd, udp_input, 143124861Sharti p, NULL)) == NULL) { 144124861Sharti close(p->input.fd); 145124861Sharti p->input.fd = -1; 146124861Sharti return (SNMP_ERR_GENERR); 147124861Sharti } 148124861Sharti return (SNMP_ERR_NOERROR); 149124861Sharti} 150124861Sharti 151124861Sharti/* 152124861Sharti * Create a new SNMP Port object and start it, if we are not 153150920Sharti * in initialization mode. The arguments are in host byte order. 154124861Sharti */ 155124861Shartistatic int 156124861Shartiudp_open_port(u_int8_t *addr, u_int32_t udp_port, struct udp_port **pp) 157124861Sharti{ 158124861Sharti struct udp_port *port; 159124861Sharti int err; 160124861Sharti 161124861Sharti if (udp_port > 0xffff) 162124861Sharti return (SNMP_ERR_NO_CREATION); 163124861Sharti if ((port = malloc(sizeof(*port))) == NULL) 164124861Sharti return (SNMP_ERR_GENERR); 165124861Sharti memset(port, 0, sizeof(*port)); 166124861Sharti 167124861Sharti /* initialize common part */ 168124861Sharti port->tport.index.len = 5; 169124861Sharti port->tport.index.subs[0] = addr[0]; 170124861Sharti port->tport.index.subs[1] = addr[1]; 171124861Sharti port->tport.index.subs[2] = addr[2]; 172124861Sharti port->tport.index.subs[3] = addr[3]; 173124861Sharti port->tport.index.subs[4] = udp_port; 174124861Sharti 175124861Sharti port->addr[0] = addr[0]; 176124861Sharti port->addr[1] = addr[1]; 177124861Sharti port->addr[2] = addr[2]; 178124861Sharti port->addr[3] = addr[3]; 179124861Sharti port->port = udp_port; 180124861Sharti 181124861Sharti port->input.fd = -1; 182124861Sharti port->input.id = NULL; 183124861Sharti port->input.stream = 0; 184124861Sharti port->input.cred = 0; 185124861Sharti port->input.peer = (struct sockaddr *)&port->ret; 186124861Sharti port->input.peerlen = sizeof(port->ret); 187124861Sharti 188124861Sharti trans_insert_port(my_trans, &port->tport); 189124861Sharti 190124861Sharti if (community != COMM_INITIALIZE && 191124861Sharti (err = udp_init_port(&port->tport)) != SNMP_ERR_NOERROR) { 192124861Sharti udp_close_port(&port->tport); 193124861Sharti return (err); 194124861Sharti } 195124861Sharti *pp = port; 196124861Sharti return (SNMP_ERR_NOERROR); 197124861Sharti} 198124861Sharti 199124861Sharti/* 200124861Sharti * Close an SNMP port 201124861Sharti */ 202124861Shartistatic void 203124861Shartiudp_close_port(struct tport *tp) 204124861Sharti{ 205124861Sharti struct udp_port *port = (struct udp_port *)tp; 206124861Sharti 207124861Sharti snmpd_input_close(&port->input); 208124861Sharti trans_remove_port(tp); 209124861Sharti free(port); 210124861Sharti} 211124861Sharti 212124861Sharti/* 213124861Sharti * Send something 214124861Sharti */ 215124861Shartistatic ssize_t 216124861Shartiudp_send(struct tport *tp, const u_char *buf, size_t len, 217124861Sharti const struct sockaddr *addr, size_t addrlen) 218124861Sharti{ 219124861Sharti struct udp_port *p = (struct udp_port *)tp; 220312264Sngie struct cmsghdr *cmsg; 221312264Sngie struct in_addr *src_addr; 222312264Sngie struct msghdr msg; 223312264Sngie char cbuf[CMSG_SPACE(sizeof(struct in_addr))]; 224312264Sngie struct iovec iov; 225124861Sharti 226312264Sngie iov.iov_base = __DECONST(void*, buf); 227312264Sngie iov.iov_len = len; 228312264Sngie 229312267Sngie msg.msg_flags = 0; 230312264Sngie msg.msg_iov = &iov; 231312264Sngie msg.msg_iovlen = 1; 232312264Sngie msg.msg_name = __DECONST(void *, addr); 233312264Sngie msg.msg_namelen = addrlen; 234312264Sngie msg.msg_control = cbuf; 235312264Sngie msg.msg_controllen = sizeof(cbuf); 236312264Sngie 237312264Sngie cmsg = CMSG_FIRSTHDR(&msg); 238312264Sngie cmsg->cmsg_level = IPPROTO_IP; 239312264Sngie cmsg->cmsg_type = IP_SENDSRCADDR; 240312264Sngie cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_addr)); 241312264Sngie src_addr = (struct in_addr *)(void*)CMSG_DATA(cmsg); 242312264Sngie memcpy(src_addr, &p->recv_addr, sizeof(struct in_addr)); 243312264Sngie 244312264Sngie return (sendmsg(p->input.fd, &msg, 0)); 245124861Sharti} 246124861Sharti 247312057Sngiestatic void 248312057Sngiecheck_priv_dgram(struct port_input *pi, struct sockcred *cred) 249312057Sngie{ 250312057Sngie 251312057Sngie /* process explicitly sends credentials */ 252312057Sngie if (cred) 253312057Sngie pi->priv = (cred->sc_euid == 0); 254312057Sngie else 255312057Sngie pi->priv = 0; 256312057Sngie} 257312057Sngie 258124861Sharti/* 259312057Sngie * Input from a datagram socket. 260312057Sngie * Each receive should return one datagram. 261312057Sngie */ 262312057Sngiestatic ssize_t 263312057Sngierecv_dgram(struct port_input *pi, struct in_addr *laddr) 264312057Sngie{ 265312057Sngie u_char embuf[1000]; 266312057Sngie char cbuf[CMSG_SPACE(SOCKCREDSIZE(CMGROUP_MAX)) + 267312057Sngie CMSG_SPACE(sizeof(struct in_addr))]; 268312057Sngie struct msghdr msg; 269312057Sngie struct iovec iov[1]; 270312057Sngie ssize_t len; 271312057Sngie struct cmsghdr *cmsg; 272312057Sngie struct sockcred *cred = NULL; 273312057Sngie 274312057Sngie if (pi->buf == NULL) { 275312057Sngie /* no buffer yet - allocate one */ 276312057Sngie if ((pi->buf = buf_alloc(0)) == NULL) { 277312057Sngie /* ups - could not get buffer. Read away input 278312057Sngie * and drop it */ 279312057Sngie (void)recvfrom(pi->fd, embuf, sizeof(embuf), 280312057Sngie 0, NULL, NULL); 281312057Sngie /* return error */ 282312057Sngie return (-1); 283312057Sngie } 284312057Sngie pi->buflen = buf_size(0); 285312057Sngie } 286312057Sngie 287312057Sngie /* try to get a message */ 288312057Sngie msg.msg_name = pi->peer; 289312057Sngie msg.msg_namelen = pi->peerlen; 290312057Sngie msg.msg_iov = iov; 291312057Sngie msg.msg_iovlen = 1; 292312057Sngie memset(cbuf, 0, sizeof(cbuf)); 293312057Sngie msg.msg_control = cbuf; 294312057Sngie msg.msg_controllen = sizeof(cbuf); 295312057Sngie msg.msg_flags = 0; 296312057Sngie 297312057Sngie iov[0].iov_base = pi->buf; 298312057Sngie iov[0].iov_len = pi->buflen; 299312057Sngie 300312057Sngie len = recvmsg(pi->fd, &msg, 0); 301312057Sngie 302312057Sngie if (len == -1 || len == 0) 303312057Sngie /* receive error */ 304312057Sngie return (-1); 305312057Sngie 306312057Sngie if (msg.msg_flags & MSG_TRUNC) { 307312057Sngie /* truncated - drop */ 308312057Sngie snmpd_stats.silentDrops++; 309312057Sngie snmpd_stats.inTooLong++; 310312057Sngie return (-1); 311312057Sngie } 312312057Sngie 313312057Sngie pi->length = (size_t)len; 314312057Sngie 315312057Sngie for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; 316312057Sngie cmsg = CMSG_NXTHDR(&msg, cmsg)) { 317312057Sngie if (cmsg->cmsg_level == IPPROTO_IP && 318312057Sngie cmsg->cmsg_type == IP_RECVDSTADDR) 319312057Sngie memcpy(laddr, CMSG_DATA(cmsg), sizeof(struct in_addr)); 320312057Sngie if (cmsg->cmsg_level == SOL_SOCKET && 321312057Sngie cmsg->cmsg_type == SCM_CREDS) 322312057Sngie cred = (struct sockcred *)CMSG_DATA(cmsg); 323312057Sngie } 324312057Sngie 325312057Sngie if (pi->cred) 326312057Sngie check_priv_dgram(pi, cred); 327312057Sngie 328312057Sngie return (0); 329312057Sngie} 330312057Sngie 331312057Sngie/* 332312057Sngie * Receive something 333312057Sngie */ 334312057Sngiestatic ssize_t 335312264Sngieudp_recv(struct tport *tp, struct port_input *pi) 336312057Sngie{ 337312264Sngie struct udp_port *p = (struct udp_port *)tp; 338312267Sngie struct cmsghdr *cmsgp; 339312057Sngie struct in_addr *laddr; 340312057Sngie struct msghdr msg; 341312057Sngie char cbuf[CMSG_SPACE(sizeof(struct in_addr))]; 342312057Sngie ssize_t ret; 343312057Sngie 344312057Sngie memset(cbuf, 0, sizeof(cbuf)); 345312057Sngie 346312057Sngie msg.msg_control = cbuf; 347312057Sngie msg.msg_controllen = sizeof(cbuf); 348312057Sngie 349312057Sngie cmsgp = CMSG_FIRSTHDR(&msg); 350312057Sngie cmsgp->cmsg_len = CMSG_LEN(sizeof(struct in_addr)); 351312057Sngie cmsgp->cmsg_level = IPPROTO_IP; 352312057Sngie cmsgp->cmsg_type = IP_SENDSRCADDR; 353312057Sngie laddr = (struct in_addr *)CMSG_DATA(cmsgp); 354312057Sngie 355312057Sngie ret = recv_dgram(pi, laddr); 356312057Sngie 357312264Sngie memcpy(&p->recv_addr, laddr, sizeof(struct in_addr)); 358312264Sngie 359312057Sngie if (laddr->s_addr == INADDR_ANY) { 360312057Sngie msg.msg_control = NULL; 361312057Sngie msg.msg_controllen = 0; 362312057Sngie } 363312057Sngie 364312057Sngie return (ret); 365312057Sngie} 366312057Sngie 367312057Sngie/* 368124861Sharti * Port table 369124861Sharti */ 370124861Shartiint 371124861Shartiop_snmp_port(struct snmp_context *ctx, struct snmp_value *value, 372124861Sharti u_int sub, u_int iidx, enum snmp_op op) 373124861Sharti{ 374124861Sharti asn_subid_t which = value->var.subs[sub-1]; 375124861Sharti struct udp_port *p; 376124861Sharti u_int8_t addr[4]; 377124861Sharti u_int32_t port; 378124861Sharti 379124861Sharti switch (op) { 380124861Sharti 381124861Sharti case SNMP_OP_GETNEXT: 382124861Sharti if ((p = (struct udp_port *)trans_next_port(my_trans, 383124861Sharti &value->var, sub)) == NULL) 384124861Sharti return (SNMP_ERR_NOSUCHNAME); 385124861Sharti index_append(&value->var, sub, &p->tport.index); 386124861Sharti break; 387124861Sharti 388124861Sharti case SNMP_OP_GET: 389124861Sharti if ((p = (struct udp_port *)trans_find_port(my_trans, 390124861Sharti &value->var, sub)) == NULL) 391124861Sharti return (SNMP_ERR_NOSUCHNAME); 392124861Sharti break; 393124861Sharti 394124861Sharti case SNMP_OP_SET: 395124861Sharti p = (struct udp_port *)trans_find_port(my_trans, 396124861Sharti &value->var, sub); 397124861Sharti ctx->scratch->int1 = (p != NULL); 398124861Sharti 399124861Sharti if (which != LEAF_begemotSnmpdPortStatus) 400124861Sharti abort(); 401124861Sharti if (!TRUTH_OK(value->v.integer)) 402124861Sharti return (SNMP_ERR_WRONG_VALUE); 403124861Sharti 404124861Sharti ctx->scratch->int2 = TRUTH_GET(value->v.integer); 405124861Sharti 406124861Sharti if (ctx->scratch->int2) { 407124861Sharti /* open an SNMP port */ 408124861Sharti if (p != NULL) 409124861Sharti /* already open - do nothing */ 410124861Sharti return (SNMP_ERR_NOERROR); 411124861Sharti 412124861Sharti if (index_decode(&value->var, sub, iidx, addr, &port)) 413124861Sharti return (SNMP_ERR_NO_CREATION); 414124861Sharti return (udp_open_port(addr, port, &p)); 415124861Sharti 416124861Sharti } else { 417124861Sharti /* close SNMP port - do in commit */ 418124861Sharti } 419124861Sharti return (SNMP_ERR_NOERROR); 420124861Sharti 421124861Sharti case SNMP_OP_ROLLBACK: 422124861Sharti p = (struct udp_port *)trans_find_port(my_trans, 423124861Sharti &value->var, sub); 424124861Sharti if (ctx->scratch->int1 == 0) { 425124861Sharti /* did not exist */ 426124861Sharti if (ctx->scratch->int2 == 1) { 427124861Sharti /* created */ 428124861Sharti if (p != NULL) 429124861Sharti udp_close_port(&p->tport); 430124861Sharti } 431124861Sharti } 432124861Sharti return (SNMP_ERR_NOERROR); 433124861Sharti 434124861Sharti case SNMP_OP_COMMIT: 435124861Sharti p = (struct udp_port *)trans_find_port(my_trans, 436124861Sharti &value->var, sub); 437124861Sharti if (ctx->scratch->int1 == 1) { 438124861Sharti /* did exist */ 439124861Sharti if (ctx->scratch->int2 == 0) { 440124861Sharti /* delete */ 441124861Sharti if (p != NULL) 442124861Sharti udp_close_port(&p->tport); 443124861Sharti } 444124861Sharti } 445124861Sharti return (SNMP_ERR_NOERROR); 446124861Sharti 447124861Sharti default: 448124861Sharti abort(); 449124861Sharti } 450124861Sharti 451124861Sharti /* 452124861Sharti * Come here to fetch the value 453124861Sharti */ 454124861Sharti switch (which) { 455124861Sharti 456124861Sharti case LEAF_begemotSnmpdPortStatus: 457124861Sharti value->v.integer = 1; 458124861Sharti break; 459124861Sharti 460124861Sharti default: 461124861Sharti abort(); 462124861Sharti } 463124861Sharti 464124861Sharti return (SNMP_ERR_NOERROR); 465124861Sharti} 466