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> 7310903Sngie * 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. 16310903Sngie * 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> 35312058Sngie#include <sys/ucred.h> 36124861Sharti 37312516Sngie#include <stdbool.h> 38124861Sharti#include <stdlib.h> 39124861Sharti#include <syslog.h> 40124861Sharti#include <string.h> 41124861Sharti#include <errno.h> 42124861Sharti#include <unistd.h> 43124861Sharti 44124861Sharti#include <netinet/in.h> 45124861Sharti#include <arpa/inet.h> 46124861Sharti 47124861Sharti#include "snmpmod.h" 48124861Sharti#include "snmpd.h" 49124861Sharti#include "trans_udp.h" 50124861Sharti#include "tree.h" 51124861Sharti#include "oid.h" 52124861Sharti 53124861Shartistatic int udp_start(void); 54124861Shartistatic int udp_stop(int); 55124861Shartistatic void udp_close_port(struct tport *); 56124861Shartistatic int udp_init_port(struct tport *); 57124861Shartistatic ssize_t udp_send(struct tport *, const u_char *, size_t, 58124861Sharti const struct sockaddr *, size_t); 59312265Sngiestatic ssize_t udp_recv(struct tport *, struct port_input *); 60124861Sharti 61124861Sharti/* exported */ 62124861Sharticonst struct transport_def udp_trans = { 63124861Sharti "udp", 64124861Sharti OIDX_begemotSnmpdTransUdp, 65124861Sharti udp_start, 66124861Sharti udp_stop, 67124861Sharti udp_close_port, 68124861Sharti udp_init_port, 69312058Sngie udp_send, 70312058Sngie udp_recv 71124861Sharti}; 72124861Shartistatic struct transport *my_trans; 73124861Sharti 74124861Shartistatic int 75124861Shartiudp_start(void) 76124861Sharti{ 77124861Sharti return (trans_register(&udp_trans, &my_trans)); 78124861Sharti} 79124861Sharti 80124861Shartistatic int 81124861Shartiudp_stop(int force __unused) 82124861Sharti{ 83124861Sharti if (my_trans != NULL) 84124861Sharti if (trans_unregister(my_trans) != 0) 85124861Sharti return (SNMP_ERR_GENERR); 86124861Sharti return (SNMP_ERR_NOERROR); 87124861Sharti} 88124861Sharti 89124861Sharti/* 90124861Sharti * A UDP port is ready 91124861Sharti */ 92124861Shartistatic void 93124861Shartiudp_input(int fd __unused, void *udata) 94124861Sharti{ 95124861Sharti struct udp_port *p = udata; 96124861Sharti 97124861Sharti p->input.peerlen = sizeof(p->ret); 98124861Sharti snmpd_input(&p->input, &p->tport); 99124861Sharti} 100124861Sharti 101124861Sharti/* 102124861Sharti * Create a UDP socket and bind it to the given port 103124861Sharti */ 104124861Shartistatic int 105124861Shartiudp_init_port(struct tport *tp) 106124861Sharti{ 107124861Sharti struct udp_port *p = (struct udp_port *)tp; 108124861Sharti struct sockaddr_in addr; 109124861Sharti u_int32_t ip; 110240271Sglebius const int on = 1; 111124861Sharti 112124861Sharti if ((p->input.fd = socket(PF_INET, SOCK_DGRAM, 0)) < 0) { 113124861Sharti syslog(LOG_ERR, "creating UDP socket: %m"); 114124861Sharti return (SNMP_ERR_RES_UNAVAIL); 115124861Sharti } 116124861Sharti ip = (p->addr[0] << 24) | (p->addr[1] << 16) | (p->addr[2] << 8) | 117124861Sharti p->addr[3]; 118124861Sharti memset(&addr, 0, sizeof(addr)); 119124861Sharti addr.sin_addr.s_addr = htonl(ip); 120124861Sharti addr.sin_port = htons(p->port); 121124861Sharti addr.sin_family = AF_INET; 122124861Sharti addr.sin_len = sizeof(addr); 123312516Sngie if (addr.sin_addr.s_addr == INADDR_ANY) { 124312516Sngie if (setsockopt(p->input.fd, IPPROTO_IP, IP_RECVDSTADDR, &on, 125312516Sngie sizeof(on)) == -1) { 126312516Sngie syslog(LOG_ERR, "setsockopt(IP_RECVDSTADDR): %m"); 127312516Sngie close(p->input.fd); 128312516Sngie p->input.fd = -1; 129312516Sngie return (SNMP_ERR_GENERR); 130312516Sngie } 131312516Sngie p->recvdstaddr = true; 132240734Sglebius } 133124861Sharti if (bind(p->input.fd, (struct sockaddr *)&addr, sizeof(addr))) { 134124861Sharti if (errno == EADDRNOTAVAIL) { 135124861Sharti close(p->input.fd); 136124861Sharti p->input.fd = -1; 137124861Sharti return (SNMP_ERR_INCONS_NAME); 138124861Sharti } 139124861Sharti syslog(LOG_ERR, "bind: %s:%u %m", inet_ntoa(addr.sin_addr), 140124861Sharti p->port); 141124861Sharti close(p->input.fd); 142124861Sharti p->input.fd = -1; 143124861Sharti return (SNMP_ERR_GENERR); 144124861Sharti } 145124861Sharti if ((p->input.id = fd_select(p->input.fd, udp_input, 146124861Sharti p, NULL)) == NULL) { 147124861Sharti close(p->input.fd); 148124861Sharti p->input.fd = -1; 149124861Sharti return (SNMP_ERR_GENERR); 150124861Sharti } 151124861Sharti return (SNMP_ERR_NOERROR); 152124861Sharti} 153124861Sharti 154124861Sharti/* 155124861Sharti * Create a new SNMP Port object and start it, if we are not 156150920Sharti * in initialization mode. The arguments are in host byte order. 157124861Sharti */ 158124861Shartistatic int 159124861Shartiudp_open_port(u_int8_t *addr, u_int32_t udp_port, struct udp_port **pp) 160124861Sharti{ 161124861Sharti struct udp_port *port; 162124861Sharti int err; 163124861Sharti 164124861Sharti if (udp_port > 0xffff) 165124861Sharti return (SNMP_ERR_NO_CREATION); 166124861Sharti if ((port = malloc(sizeof(*port))) == NULL) 167124861Sharti return (SNMP_ERR_GENERR); 168124861Sharti memset(port, 0, sizeof(*port)); 169124861Sharti 170124861Sharti /* initialize common part */ 171124861Sharti port->tport.index.len = 5; 172124861Sharti port->tport.index.subs[0] = addr[0]; 173124861Sharti port->tport.index.subs[1] = addr[1]; 174124861Sharti port->tport.index.subs[2] = addr[2]; 175124861Sharti port->tport.index.subs[3] = addr[3]; 176124861Sharti port->tport.index.subs[4] = udp_port; 177124861Sharti 178124861Sharti port->addr[0] = addr[0]; 179124861Sharti port->addr[1] = addr[1]; 180124861Sharti port->addr[2] = addr[2]; 181124861Sharti port->addr[3] = addr[3]; 182124861Sharti port->port = udp_port; 183124861Sharti 184124861Sharti port->input.fd = -1; 185124861Sharti port->input.id = NULL; 186124861Sharti port->input.stream = 0; 187124861Sharti port->input.cred = 0; 188124861Sharti port->input.peer = (struct sockaddr *)&port->ret; 189124861Sharti port->input.peerlen = sizeof(port->ret); 190124861Sharti 191124861Sharti trans_insert_port(my_trans, &port->tport); 192124861Sharti 193124861Sharti if (community != COMM_INITIALIZE && 194124861Sharti (err = udp_init_port(&port->tport)) != SNMP_ERR_NOERROR) { 195124861Sharti udp_close_port(&port->tport); 196124861Sharti return (err); 197124861Sharti } 198124861Sharti *pp = port; 199124861Sharti return (SNMP_ERR_NOERROR); 200124861Sharti} 201124861Sharti 202124861Sharti/* 203124861Sharti * Close an SNMP port 204124861Sharti */ 205124861Shartistatic void 206124861Shartiudp_close_port(struct tport *tp) 207124861Sharti{ 208124861Sharti struct udp_port *port = (struct udp_port *)tp; 209124861Sharti 210124861Sharti snmpd_input_close(&port->input); 211124861Sharti trans_remove_port(tp); 212124861Sharti free(port); 213124861Sharti} 214124861Sharti 215124861Sharti/* 216124861Sharti * Send something 217124861Sharti */ 218124861Shartistatic ssize_t 219124861Shartiudp_send(struct tport *tp, const u_char *buf, size_t len, 220124861Sharti const struct sockaddr *addr, size_t addrlen) 221124861Sharti{ 222124861Sharti struct udp_port *p = (struct udp_port *)tp; 223312265Sngie struct cmsghdr *cmsg; 224312265Sngie struct msghdr msg; 225312265Sngie char cbuf[CMSG_SPACE(sizeof(struct in_addr))]; 226312265Sngie struct iovec iov; 227124861Sharti 228312265Sngie iov.iov_base = __DECONST(void*, buf); 229312265Sngie iov.iov_len = len; 230312265Sngie 231312266Sngie msg.msg_flags = 0; 232312265Sngie msg.msg_iov = &iov; 233312265Sngie msg.msg_iovlen = 1; 234312265Sngie msg.msg_name = __DECONST(void *, addr); 235312265Sngie msg.msg_namelen = addrlen; 236312265Sngie 237312516Sngie if (p->recvdstaddr) { 238312516Sngie msg.msg_control = cbuf; 239312516Sngie msg.msg_controllen = sizeof(cbuf); 240312265Sngie 241312516Sngie cmsg = CMSG_FIRSTHDR(&msg); 242312516Sngie cmsg->cmsg_level = IPPROTO_IP; 243312516Sngie cmsg->cmsg_type = IP_SENDSRCADDR; 244312516Sngie cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_addr)); 245312516Sngie memcpy(CMSG_DATA(cmsg), &p->dstaddr, sizeof(struct in_addr)); 246312516Sngie } else { 247312516Sngie msg.msg_control = NULL; 248312516Sngie msg.msg_controllen = 0; 249312516Sngie } 250312516Sngie 251312265Sngie return (sendmsg(p->input.fd, &msg, 0)); 252124861Sharti} 253124861Sharti 254312058Sngiestatic void 255312058Sngiecheck_priv_dgram(struct port_input *pi, struct sockcred *cred) 256312058Sngie{ 257312058Sngie 258312058Sngie /* process explicitly sends credentials */ 259312058Sngie if (cred) 260312058Sngie pi->priv = (cred->sc_euid == 0); 261312058Sngie else 262312058Sngie pi->priv = 0; 263312058Sngie} 264312058Sngie 265124861Sharti/* 266312058Sngie * Input from a datagram socket. 267312058Sngie * Each receive should return one datagram. 268312058Sngie */ 269312058Sngiestatic ssize_t 270312516Sngieudp_recv(struct tport *tp, struct port_input *pi) 271312058Sngie{ 272312058Sngie u_char embuf[1000]; 273312058Sngie char cbuf[CMSG_SPACE(SOCKCREDSIZE(CMGROUP_MAX)) + 274312058Sngie CMSG_SPACE(sizeof(struct in_addr))]; 275312516Sngie struct udp_port *p = (struct udp_port *)tp; 276312058Sngie struct msghdr msg; 277312058Sngie struct iovec iov[1]; 278312058Sngie ssize_t len; 279312058Sngie struct cmsghdr *cmsg; 280312058Sngie struct sockcred *cred = NULL; 281312058Sngie 282312058Sngie if (pi->buf == NULL) { 283312058Sngie /* no buffer yet - allocate one */ 284312058Sngie if ((pi->buf = buf_alloc(0)) == NULL) { 285312058Sngie /* ups - could not get buffer. Read away input 286312058Sngie * and drop it */ 287312058Sngie (void)recvfrom(pi->fd, embuf, sizeof(embuf), 288312058Sngie 0, NULL, NULL); 289312058Sngie /* return error */ 290312058Sngie return (-1); 291312058Sngie } 292312058Sngie pi->buflen = buf_size(0); 293312058Sngie } 294312058Sngie 295312058Sngie /* try to get a message */ 296312058Sngie msg.msg_name = pi->peer; 297312058Sngie msg.msg_namelen = pi->peerlen; 298312058Sngie msg.msg_iov = iov; 299312058Sngie msg.msg_iovlen = 1; 300312058Sngie memset(cbuf, 0, sizeof(cbuf)); 301312058Sngie msg.msg_control = cbuf; 302312058Sngie msg.msg_controllen = sizeof(cbuf); 303312058Sngie msg.msg_flags = 0; 304312058Sngie 305312058Sngie iov[0].iov_base = pi->buf; 306312058Sngie iov[0].iov_len = pi->buflen; 307312058Sngie 308312058Sngie len = recvmsg(pi->fd, &msg, 0); 309312058Sngie 310312058Sngie if (len == -1 || len == 0) 311312058Sngie /* receive error */ 312312058Sngie return (-1); 313312058Sngie 314312058Sngie if (msg.msg_flags & MSG_TRUNC) { 315312058Sngie /* truncated - drop */ 316312058Sngie snmpd_stats.silentDrops++; 317312058Sngie snmpd_stats.inTooLong++; 318312058Sngie return (-1); 319312058Sngie } 320312058Sngie 321312058Sngie pi->length = (size_t)len; 322312058Sngie 323312058Sngie for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; 324312058Sngie cmsg = CMSG_NXTHDR(&msg, cmsg)) { 325312058Sngie if (cmsg->cmsg_level == IPPROTO_IP && 326312058Sngie cmsg->cmsg_type == IP_RECVDSTADDR) 327312516Sngie memcpy(&p->dstaddr, CMSG_DATA(cmsg), 328312516Sngie sizeof(struct in_addr)); 329312058Sngie if (cmsg->cmsg_level == SOL_SOCKET && 330312058Sngie cmsg->cmsg_type == SCM_CREDS) 331312058Sngie cred = (struct sockcred *)CMSG_DATA(cmsg); 332312058Sngie } 333312058Sngie 334312058Sngie if (pi->cred) 335312058Sngie check_priv_dgram(pi, cred); 336312058Sngie 337312058Sngie return (0); 338312058Sngie} 339312058Sngie 340312058Sngie/* 341124861Sharti * Port table 342124861Sharti */ 343124861Shartiint 344124861Shartiop_snmp_port(struct snmp_context *ctx, struct snmp_value *value, 345124861Sharti u_int sub, u_int iidx, enum snmp_op op) 346124861Sharti{ 347124861Sharti asn_subid_t which = value->var.subs[sub-1]; 348124861Sharti struct udp_port *p; 349124861Sharti u_int8_t addr[4]; 350124861Sharti u_int32_t port; 351124861Sharti 352124861Sharti switch (op) { 353124861Sharti 354124861Sharti case SNMP_OP_GETNEXT: 355124861Sharti if ((p = (struct udp_port *)trans_next_port(my_trans, 356124861Sharti &value->var, sub)) == NULL) 357124861Sharti return (SNMP_ERR_NOSUCHNAME); 358124861Sharti index_append(&value->var, sub, &p->tport.index); 359124861Sharti break; 360124861Sharti 361124861Sharti case SNMP_OP_GET: 362124861Sharti if ((p = (struct udp_port *)trans_find_port(my_trans, 363124861Sharti &value->var, sub)) == NULL) 364124861Sharti return (SNMP_ERR_NOSUCHNAME); 365124861Sharti break; 366124861Sharti 367124861Sharti case SNMP_OP_SET: 368124861Sharti p = (struct udp_port *)trans_find_port(my_trans, 369124861Sharti &value->var, sub); 370124861Sharti ctx->scratch->int1 = (p != NULL); 371124861Sharti 372124861Sharti if (which != LEAF_begemotSnmpdPortStatus) 373124861Sharti abort(); 374124861Sharti if (!TRUTH_OK(value->v.integer)) 375124861Sharti return (SNMP_ERR_WRONG_VALUE); 376124861Sharti 377124861Sharti ctx->scratch->int2 = TRUTH_GET(value->v.integer); 378124861Sharti 379124861Sharti if (ctx->scratch->int2) { 380124861Sharti /* open an SNMP port */ 381124861Sharti if (p != NULL) 382124861Sharti /* already open - do nothing */ 383124861Sharti return (SNMP_ERR_NOERROR); 384124861Sharti 385124861Sharti if (index_decode(&value->var, sub, iidx, addr, &port)) 386124861Sharti return (SNMP_ERR_NO_CREATION); 387124861Sharti return (udp_open_port(addr, port, &p)); 388124861Sharti 389124861Sharti } else { 390124861Sharti /* close SNMP port - do in commit */ 391124861Sharti } 392124861Sharti return (SNMP_ERR_NOERROR); 393124861Sharti 394124861Sharti case SNMP_OP_ROLLBACK: 395124861Sharti p = (struct udp_port *)trans_find_port(my_trans, 396124861Sharti &value->var, sub); 397124861Sharti if (ctx->scratch->int1 == 0) { 398124861Sharti /* did not exist */ 399124861Sharti if (ctx->scratch->int2 == 1) { 400124861Sharti /* created */ 401124861Sharti if (p != NULL) 402124861Sharti udp_close_port(&p->tport); 403124861Sharti } 404124861Sharti } 405124861Sharti return (SNMP_ERR_NOERROR); 406124861Sharti 407124861Sharti case SNMP_OP_COMMIT: 408124861Sharti p = (struct udp_port *)trans_find_port(my_trans, 409124861Sharti &value->var, sub); 410124861Sharti if (ctx->scratch->int1 == 1) { 411124861Sharti /* did exist */ 412124861Sharti if (ctx->scratch->int2 == 0) { 413124861Sharti /* delete */ 414124861Sharti if (p != NULL) 415124861Sharti udp_close_port(&p->tport); 416124861Sharti } 417124861Sharti } 418124861Sharti return (SNMP_ERR_NOERROR); 419124861Sharti 420124861Sharti default: 421124861Sharti abort(); 422124861Sharti } 423124861Sharti 424124861Sharti /* 425124861Sharti * Come here to fetch the value 426124861Sharti */ 427124861Sharti switch (which) { 428124861Sharti 429124861Sharti case LEAF_begemotSnmpdPortStatus: 430124861Sharti value->v.integer = 1; 431124861Sharti break; 432124861Sharti 433124861Sharti default: 434124861Sharti abort(); 435124861Sharti } 436124861Sharti 437124861Sharti return (SNMP_ERR_NOERROR); 438124861Sharti} 439