1 /* 2 * Copyright (c) 2003 3 * Fraunhofer Institute for Open Communication Systems (FhG Fokus). 4 * All rights reserved. 5 * 6 * Author: Harti Brandt <harti@freebsd.org> 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 * 29 * $Begemot: bsnmp/snmpd/trans_udp.c,v 1.5 2005/10/04 08:46:56 brandt_h Exp $ 30 * 31 * UDP transport 32 */ 33 #include <sys/types.h> 34 #include <sys/queue.h> 35 #include <sys/ucred.h> 36 37 #include <stdbool.h> 38 #include <stdlib.h> 39 #include <syslog.h> 40 #include <string.h> 41 #include <errno.h> 42 #include <unistd.h> 43 44 #include <netinet/in.h> 45 #include <arpa/inet.h> 46 47 #include "snmpmod.h" 48 #include "snmpd.h" 49 #include "trans_udp.h" 50 #include "tree.h" 51 #include "oid.h" 52 53 static int udp_start(void); 54 static int udp_stop(int); 55 static void udp_close_port(struct tport *); 56 static int udp_init_port(struct tport *); 57 static ssize_t udp_send(struct tport *, const u_char *, size_t, 58 const struct sockaddr *, size_t); 59 static ssize_t udp_recv(struct tport *, struct port_input *); 60 61 /* exported */ 62 const struct transport_def udp_trans = { 63 "udp", 64 OIDX_begemotSnmpdTransUdp, 65 udp_start, 66 udp_stop, 67 udp_close_port, 68 udp_init_port, 69 udp_send, 70 udp_recv, 71 NULL 72 }; 73 static struct transport *my_trans; 74 75 static int 76 udp_start(void) 77 { 78 return (trans_register(&udp_trans, &my_trans)); 79 } 80 81 static int 82 udp_stop(int force __unused) 83 { 84 if (my_trans != NULL) 85 if (trans_unregister(my_trans) != 0) 86 return (SNMP_ERR_GENERR); 87 return (SNMP_ERR_NOERROR); 88 } 89 90 /* 91 * A UDP port is ready 92 */ 93 static void 94 udp_input(int fd __unused, void *udata) 95 { 96 struct udp_port *p = udata; 97 98 p->input.peerlen = sizeof(p->ret); 99 snmpd_input(&p->input, &p->tport); 100 } 101 102 /* 103 * Create a UDP socket and bind it to the given port 104 */ 105 static int 106 udp_init_port(struct tport *tp) 107 { 108 struct udp_port *p = (struct udp_port *)tp; 109 struct sockaddr_in addr; 110 u_int32_t ip; 111 const int on = 1; 112 113 if ((p->input.fd = socket(PF_INET, SOCK_DGRAM, 0)) < 0) { 114 syslog(LOG_ERR, "creating UDP socket: %m"); 115 return (SNMP_ERR_RES_UNAVAIL); 116 } 117 ip = (p->addr[0] << 24) | (p->addr[1] << 16) | (p->addr[2] << 8) | 118 p->addr[3]; 119 memset(&addr, 0, sizeof(addr)); 120 addr.sin_addr.s_addr = htonl(ip); 121 addr.sin_port = htons(p->port); 122 addr.sin_family = AF_INET; 123 addr.sin_len = sizeof(addr); 124 if (addr.sin_addr.s_addr == INADDR_ANY) { 125 if (setsockopt(p->input.fd, IPPROTO_IP, IP_RECVDSTADDR, &on, 126 sizeof(on)) == -1) { 127 syslog(LOG_ERR, "setsockopt(IP_RECVDSTADDR): %m"); 128 close(p->input.fd); 129 p->input.fd = -1; 130 return (SNMP_ERR_GENERR); 131 } 132 p->recvdstaddr = true; 133 } 134 if (bind(p->input.fd, (struct sockaddr *)&addr, sizeof(addr))) { 135 if (errno == EADDRNOTAVAIL) { 136 close(p->input.fd); 137 p->input.fd = -1; 138 return (SNMP_ERR_INCONS_NAME); 139 } 140 syslog(LOG_ERR, "bind: %s:%u %m", inet_ntoa(addr.sin_addr), 141 p->port); 142 close(p->input.fd); 143 p->input.fd = -1; 144 return (SNMP_ERR_GENERR); 145 } 146 if ((p->input.id = fd_select(p->input.fd, udp_input, 147 p, NULL)) == NULL) { 148 close(p->input.fd); 149 p->input.fd = -1; 150 return (SNMP_ERR_GENERR); 151 } 152 return (SNMP_ERR_NOERROR); 153 } 154 155 /* 156 * Create a new SNMP Port object and start it, if we are not 157 * in initialization mode. The arguments are in host byte order. 158 */ 159 static int 160 udp_open_port(u_int8_t *addr, u_int32_t udp_port, struct udp_port **pp) 161 { 162 struct udp_port *port; 163 int err; 164 165 if (udp_port > 0xffff) 166 return (SNMP_ERR_NO_CREATION); 167 if ((port = malloc(sizeof(*port))) == NULL) 168 return (SNMP_ERR_GENERR); 169 memset(port, 0, sizeof(*port)); 170 171 /* initialize common part */ 172 port->tport.index.len = 5; 173 port->tport.index.subs[0] = addr[0]; 174 port->tport.index.subs[1] = addr[1]; 175 port->tport.index.subs[2] = addr[2]; 176 port->tport.index.subs[3] = addr[3]; 177 port->tport.index.subs[4] = udp_port; 178 179 port->addr[0] = addr[0]; 180 port->addr[1] = addr[1]; 181 port->addr[2] = addr[2]; 182 port->addr[3] = addr[3]; 183 port->port = udp_port; 184 185 port->input.fd = -1; 186 port->input.id = NULL; 187 port->input.stream = 0; 188 port->input.cred = 0; 189 port->input.peer = (struct sockaddr *)&port->ret; 190 port->input.peerlen = sizeof(port->ret); 191 192 trans_insert_port(my_trans, &port->tport); 193 194 if (community != COMM_INITIALIZE && 195 (err = udp_init_port(&port->tport)) != SNMP_ERR_NOERROR) { 196 udp_close_port(&port->tport); 197 return (err); 198 } 199 *pp = port; 200 return (SNMP_ERR_NOERROR); 201 } 202 203 /* 204 * Close an SNMP port 205 */ 206 static void 207 udp_close_port(struct tport *tp) 208 { 209 struct udp_port *port = (struct udp_port *)tp; 210 211 snmpd_input_close(&port->input); 212 trans_remove_port(tp); 213 free(port); 214 } 215 216 /* 217 * Send something 218 */ 219 static ssize_t 220 udp_send(struct tport *tp, const u_char *buf, size_t len, 221 const struct sockaddr *addr, size_t addrlen) 222 { 223 struct udp_port *p = (struct udp_port *)tp; 224 struct cmsghdr *cmsg; 225 struct msghdr msg; 226 char cbuf[CMSG_SPACE(sizeof(struct in_addr))]; 227 struct iovec iov; 228 229 iov.iov_base = __DECONST(void*, buf); 230 iov.iov_len = len; 231 232 msg.msg_flags = 0; 233 msg.msg_iov = &iov; 234 msg.msg_iovlen = 1; 235 msg.msg_name = __DECONST(void *, addr); 236 msg.msg_namelen = addrlen; 237 238 if (p->recvdstaddr) { 239 msg.msg_control = cbuf; 240 msg.msg_controllen = sizeof(cbuf); 241 242 cmsg = CMSG_FIRSTHDR(&msg); 243 cmsg->cmsg_level = IPPROTO_IP; 244 cmsg->cmsg_type = IP_SENDSRCADDR; 245 cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_addr)); 246 memcpy(CMSG_DATA(cmsg), &p->dstaddr, sizeof(struct in_addr)); 247 } else { 248 msg.msg_control = NULL; 249 msg.msg_controllen = 0; 250 } 251 252 return (sendmsg(p->input.fd, &msg, 0)); 253 } 254 255 static void 256 check_priv_dgram(struct port_input *pi, struct sockcred *cred) 257 { 258 259 /* process explicitly sends credentials */ 260 if (cred) 261 pi->priv = (cred->sc_euid == 0); 262 else 263 pi->priv = 0; 264 } 265 266 /* 267 * Input from a datagram socket. 268 * Each receive should return one datagram. 269 */ 270 static ssize_t 271 udp_recv(struct tport *tp, struct port_input *pi) 272 { 273 u_char embuf[1000]; 274 char cbuf[CMSG_SPACE(SOCKCREDSIZE(CMGROUP_MAX)) + 275 CMSG_SPACE(sizeof(struct in_addr))]; 276 struct udp_port *p = (struct udp_port *)tp; 277 struct msghdr msg; 278 struct iovec iov[1]; 279 ssize_t len; 280 struct cmsghdr *cmsg; 281 struct sockcred *cred = NULL; 282 283 if (pi->buf == NULL) { 284 /* no buffer yet - allocate one */ 285 if ((pi->buf = buf_alloc(0)) == NULL) { 286 /* ups - could not get buffer. Read away input 287 * and drop it */ 288 (void)recvfrom(pi->fd, embuf, sizeof(embuf), 289 0, NULL, NULL); 290 /* return error */ 291 return (-1); 292 } 293 pi->buflen = buf_size(0); 294 } 295 296 /* try to get a message */ 297 msg.msg_name = pi->peer; 298 msg.msg_namelen = pi->peerlen; 299 msg.msg_iov = iov; 300 msg.msg_iovlen = 1; 301 memset(cbuf, 0, sizeof(cbuf)); 302 msg.msg_control = cbuf; 303 msg.msg_controllen = sizeof(cbuf); 304 msg.msg_flags = 0; 305 306 iov[0].iov_base = pi->buf; 307 iov[0].iov_len = pi->buflen; 308 309 len = recvmsg(pi->fd, &msg, 0); 310 311 if (len == -1 || len == 0) 312 /* receive error */ 313 return (-1); 314 315 if (msg.msg_flags & MSG_TRUNC) { 316 /* truncated - drop */ 317 snmpd_stats.silentDrops++; 318 snmpd_stats.inTooLong++; 319 return (-1); 320 } 321 322 pi->length = (size_t)len; 323 324 for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; 325 cmsg = CMSG_NXTHDR(&msg, cmsg)) { 326 if (cmsg->cmsg_level == IPPROTO_IP && 327 cmsg->cmsg_type == IP_RECVDSTADDR) 328 memcpy(&p->dstaddr, CMSG_DATA(cmsg), 329 sizeof(struct in_addr)); 330 if (cmsg->cmsg_level == SOL_SOCKET && 331 cmsg->cmsg_type == SCM_CREDS) 332 cred = (struct sockcred *)(void *)CMSG_DATA(cmsg); 333 } 334 335 if (pi->cred) 336 check_priv_dgram(pi, cred); 337 338 return (0); 339 } 340 341 /* 342 * Port table 343 */ 344 int 345 op_snmp_port(struct snmp_context *ctx, struct snmp_value *value, 346 u_int sub, u_int iidx, enum snmp_op op) 347 { 348 asn_subid_t which = value->var.subs[sub-1]; 349 struct udp_port *p; 350 u_int8_t addr[4]; 351 u_int32_t port; 352 353 switch (op) { 354 355 case SNMP_OP_GETNEXT: 356 if ((p = (struct udp_port *)trans_next_port(my_trans, 357 &value->var, sub)) == NULL) 358 return (SNMP_ERR_NOSUCHNAME); 359 index_append(&value->var, sub, &p->tport.index); 360 break; 361 362 case SNMP_OP_GET: 363 if ((p = (struct udp_port *)trans_find_port(my_trans, 364 &value->var, sub)) == NULL) 365 return (SNMP_ERR_NOSUCHNAME); 366 break; 367 368 case SNMP_OP_SET: 369 p = (struct udp_port *)trans_find_port(my_trans, 370 &value->var, sub); 371 ctx->scratch->int1 = (p != NULL); 372 373 if (which != LEAF_begemotSnmpdPortStatus) 374 abort(); 375 if (!TRUTH_OK(value->v.integer)) 376 return (SNMP_ERR_WRONG_VALUE); 377 378 ctx->scratch->int2 = TRUTH_GET(value->v.integer); 379 380 if (ctx->scratch->int2) { 381 /* open an SNMP port */ 382 if (p != NULL) 383 /* already open - do nothing */ 384 return (SNMP_ERR_NOERROR); 385 386 if (index_decode(&value->var, sub, iidx, addr, &port)) 387 return (SNMP_ERR_NO_CREATION); 388 return (udp_open_port(addr, port, &p)); 389 390 } else { 391 /* close SNMP port - do in commit */ 392 } 393 return (SNMP_ERR_NOERROR); 394 395 case SNMP_OP_ROLLBACK: 396 p = (struct udp_port *)trans_find_port(my_trans, 397 &value->var, sub); 398 if (ctx->scratch->int1 == 0) { 399 /* did not exist */ 400 if (ctx->scratch->int2 == 1) { 401 /* created */ 402 if (p != NULL) 403 udp_close_port(&p->tport); 404 } 405 } 406 return (SNMP_ERR_NOERROR); 407 408 case SNMP_OP_COMMIT: 409 p = (struct udp_port *)trans_find_port(my_trans, 410 &value->var, sub); 411 if (ctx->scratch->int1 == 1) { 412 /* did exist */ 413 if (ctx->scratch->int2 == 0) { 414 /* delete */ 415 if (p != NULL) 416 udp_close_port(&p->tport); 417 } 418 } 419 return (SNMP_ERR_NOERROR); 420 421 default: 422 abort(); 423 } 424 425 /* 426 * Come here to fetch the value 427 */ 428 switch (which) { 429 430 case LEAF_begemotSnmpdPortStatus: 431 value->v.integer = 1; 432 break; 433 434 default: 435 abort(); 436 } 437 438 return (SNMP_ERR_NOERROR); 439 } 440