trans_udp.c revision 312264
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 229312264Sngie msg.msg_iov = &iov; 230312264Sngie msg.msg_iovlen = 1; 231312264Sngie msg.msg_name = __DECONST(void *, addr); 232312264Sngie msg.msg_namelen = addrlen; 233312264Sngie msg.msg_control = cbuf; 234312264Sngie msg.msg_controllen = sizeof(cbuf); 235312264Sngie 236312264Sngie cmsg = CMSG_FIRSTHDR(&msg); 237312264Sngie cmsg->cmsg_level = IPPROTO_IP; 238312264Sngie cmsg->cmsg_type = IP_SENDSRCADDR; 239312264Sngie cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_addr)); 240312264Sngie src_addr = (struct in_addr *)(void*)CMSG_DATA(cmsg); 241312264Sngie memcpy(src_addr, &p->recv_addr, sizeof(struct in_addr)); 242312264Sngie 243312264Sngie return (sendmsg(p->input.fd, &msg, 0)); 244124861Sharti} 245124861Sharti 246312057Sngiestatic void 247312057Sngiecheck_priv_dgram(struct port_input *pi, struct sockcred *cred) 248312057Sngie{ 249312057Sngie 250312057Sngie /* process explicitly sends credentials */ 251312057Sngie if (cred) 252312057Sngie pi->priv = (cred->sc_euid == 0); 253312057Sngie else 254312057Sngie pi->priv = 0; 255312057Sngie} 256312057Sngie 257124861Sharti/* 258312057Sngie * Input from a datagram socket. 259312057Sngie * Each receive should return one datagram. 260312057Sngie */ 261312057Sngiestatic ssize_t 262312057Sngierecv_dgram(struct port_input *pi, struct in_addr *laddr) 263312057Sngie{ 264312057Sngie u_char embuf[1000]; 265312057Sngie char cbuf[CMSG_SPACE(SOCKCREDSIZE(CMGROUP_MAX)) + 266312057Sngie CMSG_SPACE(sizeof(struct in_addr))]; 267312057Sngie struct msghdr msg; 268312057Sngie struct iovec iov[1]; 269312057Sngie ssize_t len; 270312057Sngie struct cmsghdr *cmsg; 271312057Sngie struct sockcred *cred = NULL; 272312057Sngie 273312057Sngie if (pi->buf == NULL) { 274312057Sngie /* no buffer yet - allocate one */ 275312057Sngie if ((pi->buf = buf_alloc(0)) == NULL) { 276312057Sngie /* ups - could not get buffer. Read away input 277312057Sngie * and drop it */ 278312057Sngie (void)recvfrom(pi->fd, embuf, sizeof(embuf), 279312057Sngie 0, NULL, NULL); 280312057Sngie /* return error */ 281312057Sngie return (-1); 282312057Sngie } 283312057Sngie pi->buflen = buf_size(0); 284312057Sngie } 285312057Sngie 286312057Sngie /* try to get a message */ 287312057Sngie msg.msg_name = pi->peer; 288312057Sngie msg.msg_namelen = pi->peerlen; 289312057Sngie msg.msg_iov = iov; 290312057Sngie msg.msg_iovlen = 1; 291312057Sngie memset(cbuf, 0, sizeof(cbuf)); 292312057Sngie msg.msg_control = cbuf; 293312057Sngie msg.msg_controllen = sizeof(cbuf); 294312057Sngie msg.msg_flags = 0; 295312057Sngie 296312057Sngie iov[0].iov_base = pi->buf; 297312057Sngie iov[0].iov_len = pi->buflen; 298312057Sngie 299312057Sngie len = recvmsg(pi->fd, &msg, 0); 300312057Sngie 301312057Sngie if (len == -1 || len == 0) 302312057Sngie /* receive error */ 303312057Sngie return (-1); 304312057Sngie 305312057Sngie if (msg.msg_flags & MSG_TRUNC) { 306312057Sngie /* truncated - drop */ 307312057Sngie snmpd_stats.silentDrops++; 308312057Sngie snmpd_stats.inTooLong++; 309312057Sngie return (-1); 310312057Sngie } 311312057Sngie 312312057Sngie pi->length = (size_t)len; 313312057Sngie 314312057Sngie for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; 315312057Sngie cmsg = CMSG_NXTHDR(&msg, cmsg)) { 316312057Sngie if (cmsg->cmsg_level == IPPROTO_IP && 317312057Sngie cmsg->cmsg_type == IP_RECVDSTADDR) 318312057Sngie memcpy(laddr, CMSG_DATA(cmsg), sizeof(struct in_addr)); 319312057Sngie if (cmsg->cmsg_level == SOL_SOCKET && 320312057Sngie cmsg->cmsg_type == SCM_CREDS) 321312057Sngie cred = (struct sockcred *)CMSG_DATA(cmsg); 322312057Sngie } 323312057Sngie 324312057Sngie if (pi->cred) 325312057Sngie check_priv_dgram(pi, cred); 326312057Sngie 327312057Sngie return (0); 328312057Sngie} 329312057Sngie 330312057Sngie/* 331312057Sngie * Receive something 332312057Sngie */ 333312057Sngiestatic ssize_t 334312264Sngieudp_recv(struct tport *tp, struct port_input *pi) 335312057Sngie{ 336312264Sngie struct udp_port *p = (struct udp_port *)tp; 337312057Sngie struct in_addr *laddr; 338312057Sngie struct msghdr msg; 339312057Sngie char cbuf[CMSG_SPACE(sizeof(struct in_addr))]; 340312057Sngie struct cmsghdr *cmsgp; 341312057Sngie ssize_t ret; 342312057Sngie 343312057Sngie memset(cbuf, 0, sizeof(cbuf)); 344312057Sngie 345312057Sngie msg.msg_control = cbuf; 346312057Sngie msg.msg_controllen = sizeof(cbuf); 347312057Sngie 348312057Sngie cmsgp = CMSG_FIRSTHDR(&msg); 349312057Sngie cmsgp->cmsg_len = CMSG_LEN(sizeof(struct in_addr)); 350312057Sngie cmsgp->cmsg_level = IPPROTO_IP; 351312057Sngie cmsgp->cmsg_type = IP_SENDSRCADDR; 352312057Sngie laddr = (struct in_addr *)CMSG_DATA(cmsgp); 353312057Sngie 354312057Sngie ret = recv_dgram(pi, laddr); 355312057Sngie 356312264Sngie memcpy(&p->recv_addr, laddr, sizeof(struct in_addr)); 357312264Sngie 358312057Sngie if (laddr->s_addr == INADDR_ANY) { 359312057Sngie msg.msg_control = NULL; 360312057Sngie msg.msg_controllen = 0; 361312057Sngie } 362312057Sngie 363312057Sngie return (ret); 364312057Sngie} 365312057Sngie 366312057Sngie/* 367124861Sharti * Port table 368124861Sharti */ 369124861Shartiint 370124861Shartiop_snmp_port(struct snmp_context *ctx, struct snmp_value *value, 371124861Sharti u_int sub, u_int iidx, enum snmp_op op) 372124861Sharti{ 373124861Sharti asn_subid_t which = value->var.subs[sub-1]; 374124861Sharti struct udp_port *p; 375124861Sharti u_int8_t addr[4]; 376124861Sharti u_int32_t port; 377124861Sharti 378124861Sharti switch (op) { 379124861Sharti 380124861Sharti case SNMP_OP_GETNEXT: 381124861Sharti if ((p = (struct udp_port *)trans_next_port(my_trans, 382124861Sharti &value->var, sub)) == NULL) 383124861Sharti return (SNMP_ERR_NOSUCHNAME); 384124861Sharti index_append(&value->var, sub, &p->tport.index); 385124861Sharti break; 386124861Sharti 387124861Sharti case SNMP_OP_GET: 388124861Sharti if ((p = (struct udp_port *)trans_find_port(my_trans, 389124861Sharti &value->var, sub)) == NULL) 390124861Sharti return (SNMP_ERR_NOSUCHNAME); 391124861Sharti break; 392124861Sharti 393124861Sharti case SNMP_OP_SET: 394124861Sharti p = (struct udp_port *)trans_find_port(my_trans, 395124861Sharti &value->var, sub); 396124861Sharti ctx->scratch->int1 = (p != NULL); 397124861Sharti 398124861Sharti if (which != LEAF_begemotSnmpdPortStatus) 399124861Sharti abort(); 400124861Sharti if (!TRUTH_OK(value->v.integer)) 401124861Sharti return (SNMP_ERR_WRONG_VALUE); 402124861Sharti 403124861Sharti ctx->scratch->int2 = TRUTH_GET(value->v.integer); 404124861Sharti 405124861Sharti if (ctx->scratch->int2) { 406124861Sharti /* open an SNMP port */ 407124861Sharti if (p != NULL) 408124861Sharti /* already open - do nothing */ 409124861Sharti return (SNMP_ERR_NOERROR); 410124861Sharti 411124861Sharti if (index_decode(&value->var, sub, iidx, addr, &port)) 412124861Sharti return (SNMP_ERR_NO_CREATION); 413124861Sharti return (udp_open_port(addr, port, &p)); 414124861Sharti 415124861Sharti } else { 416124861Sharti /* close SNMP port - do in commit */ 417124861Sharti } 418124861Sharti return (SNMP_ERR_NOERROR); 419124861Sharti 420124861Sharti case SNMP_OP_ROLLBACK: 421124861Sharti p = (struct udp_port *)trans_find_port(my_trans, 422124861Sharti &value->var, sub); 423124861Sharti if (ctx->scratch->int1 == 0) { 424124861Sharti /* did not exist */ 425124861Sharti if (ctx->scratch->int2 == 1) { 426124861Sharti /* created */ 427124861Sharti if (p != NULL) 428124861Sharti udp_close_port(&p->tport); 429124861Sharti } 430124861Sharti } 431124861Sharti return (SNMP_ERR_NOERROR); 432124861Sharti 433124861Sharti case SNMP_OP_COMMIT: 434124861Sharti p = (struct udp_port *)trans_find_port(my_trans, 435124861Sharti &value->var, sub); 436124861Sharti if (ctx->scratch->int1 == 1) { 437124861Sharti /* did exist */ 438124861Sharti if (ctx->scratch->int2 == 0) { 439124861Sharti /* delete */ 440124861Sharti if (p != NULL) 441124861Sharti udp_close_port(&p->tport); 442124861Sharti } 443124861Sharti } 444124861Sharti return (SNMP_ERR_NOERROR); 445124861Sharti 446124861Sharti default: 447124861Sharti abort(); 448124861Sharti } 449124861Sharti 450124861Sharti /* 451124861Sharti * Come here to fetch the value 452124861Sharti */ 453124861Sharti switch (which) { 454124861Sharti 455124861Sharti case LEAF_begemotSnmpdPortStatus: 456124861Sharti value->v.integer = 1; 457124861Sharti break; 458124861Sharti 459124861Sharti default: 460124861Sharti abort(); 461124861Sharti } 462124861Sharti 463124861Sharti return (SNMP_ERR_NOERROR); 464124861Sharti} 465