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 }; 72 static struct transport *my_trans; 73 74 static int 75 udp_start(void) 76 { 77 return (trans_register(&udp_trans, &my_trans)); 78 } 79 80 static int 81 udp_stop(int force __unused) 82 { 83 if (my_trans != NULL) 84 if (trans_unregister(my_trans) != 0) 85 return (SNMP_ERR_GENERR); 86 return (SNMP_ERR_NOERROR); 87 } 88 89 /* 90 * A UDP port is ready 91 */ 92 static void 93 udp_input(int fd __unused, void *udata) 94 { 95 struct udp_port *p = udata; 96 97 p->input.peerlen = sizeof(p->ret); 98 snmpd_input(&p->input, &p->tport); 99 } 100 101 /* 102 * Create a UDP socket and bind it to the given port 103 */ 104 static int 105 udp_init_port(struct tport *tp) 106 { 107 struct udp_port *p = (struct udp_port *)tp; 108 struct sockaddr_in addr; 109 u_int32_t ip; 110 const int on = 1; 111 112 if ((p->input.fd = socket(PF_INET, SOCK_DGRAM, 0)) < 0) { 113 syslog(LOG_ERR, "creating UDP socket: %m"); 114 return (SNMP_ERR_RES_UNAVAIL); 115 } 116 ip = (p->addr[0] << 24) | (p->addr[1] << 16) | (p->addr[2] << 8) | 117 p->addr[3]; 118 memset(&addr, 0, sizeof(addr)); 119 addr.sin_addr.s_addr = htonl(ip); 120 addr.sin_port = htons(p->port); 121 addr.sin_family = AF_INET; 122 addr.sin_len = sizeof(addr); 123 if (addr.sin_addr.s_addr == INADDR_ANY) { 124 if (setsockopt(p->input.fd, IPPROTO_IP, IP_RECVDSTADDR, &on, 125 sizeof(on)) == -1) { 126 syslog(LOG_ERR, "setsockopt(IP_RECVDSTADDR): %m"); 127 close(p->input.fd); 128 p->input.fd = -1; 129 return (SNMP_ERR_GENERR); 130 } 131 p->recvdstaddr = true; 132 } 133 if (bind(p->input.fd, (struct sockaddr *)&addr, sizeof(addr))) { 134 if (errno == EADDRNOTAVAIL) { 135 close(p->input.fd); 136 p->input.fd = -1; 137 return (SNMP_ERR_INCONS_NAME); 138 } 139 syslog(LOG_ERR, "bind: %s:%u %m", inet_ntoa(addr.sin_addr), 140 p->port); 141 close(p->input.fd); 142 p->input.fd = -1; 143 return (SNMP_ERR_GENERR); 144 } 145 if ((p->input.id = fd_select(p->input.fd, udp_input, 146 p, NULL)) == NULL) { 147 close(p->input.fd); 148 p->input.fd = -1; 149 return (SNMP_ERR_GENERR); 150 } 151 return (SNMP_ERR_NOERROR); 152 } 153 154 /* 155 * Create a new SNMP Port object and start it, if we are not 156 * in initialization mode. The arguments are in host byte order. 157 */ 158 static int 159 udp_open_port(u_int8_t *addr, u_int32_t udp_port, struct udp_port **pp) 160 { 161 struct udp_port *port; 162 int err; 163 164 if (udp_port > 0xffff) 165 return (SNMP_ERR_NO_CREATION); 166 if ((port = malloc(sizeof(*port))) == NULL) 167 return (SNMP_ERR_GENERR); 168 memset(port, 0, sizeof(*port)); 169 170 /* initialize common part */ 171 port->tport.index.len = 5; 172 port->tport.index.subs[0] = addr[0]; 173 port->tport.index.subs[1] = addr[1]; 174 port->tport.index.subs[2] = addr[2]; 175 port->tport.index.subs[3] = addr[3]; 176 port->tport.index.subs[4] = udp_port; 177 178 port->addr[0] = addr[0]; 179 port->addr[1] = addr[1]; 180 port->addr[2] = addr[2]; 181 port->addr[3] = addr[3]; 182 port->port = udp_port; 183 184 port->input.fd = -1; 185 port->input.id = NULL; 186 port->input.stream = 0; 187 port->input.cred = 0; 188 port->input.peer = (struct sockaddr *)&port->ret; 189 port->input.peerlen = sizeof(port->ret); 190 191 trans_insert_port(my_trans, &port->tport); 192 193 if (community != COMM_INITIALIZE && 194 (err = udp_init_port(&port->tport)) != SNMP_ERR_NOERROR) { 195 udp_close_port(&port->tport); 196 return (err); 197 } 198 *pp = port; 199 return (SNMP_ERR_NOERROR); 200 } 201 202 /* 203 * Close an SNMP port 204 */ 205 static void 206 udp_close_port(struct tport *tp) 207 { 208 struct udp_port *port = (struct udp_port *)tp; 209 210 snmpd_input_close(&port->input); 211 trans_remove_port(tp); 212 free(port); 213 } 214 215 /* 216 * Send something 217 */ 218 static ssize_t 219 udp_send(struct tport *tp, const u_char *buf, size_t len, 220 const struct sockaddr *addr, size_t addrlen) 221 { 222 struct udp_port *p = (struct udp_port *)tp; 223 struct cmsghdr *cmsg; 224 struct msghdr msg; 225 char cbuf[CMSG_SPACE(sizeof(struct in_addr))]; 226 struct iovec iov; 227 228 iov.iov_base = __DECONST(void*, buf); 229 iov.iov_len = len; 230 231 msg.msg_flags = 0; 232 msg.msg_iov = &iov; 233 msg.msg_iovlen = 1; 234 msg.msg_name = __DECONST(void *, addr); 235 msg.msg_namelen = addrlen; 236 237 if (p->recvdstaddr) { 238 msg.msg_control = cbuf; 239 msg.msg_controllen = sizeof(cbuf); 240 241 cmsg = CMSG_FIRSTHDR(&msg); 242 cmsg->cmsg_level = IPPROTO_IP; 243 cmsg->cmsg_type = IP_SENDSRCADDR; 244 cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_addr)); 245 memcpy(CMSG_DATA(cmsg), &p->dstaddr, sizeof(struct in_addr)); 246 } else { 247 msg.msg_control = NULL; 248 msg.msg_controllen = 0; 249 } 250 251 return (sendmsg(p->input.fd, &msg, 0)); 252 } 253 254 static void 255 check_priv_dgram(struct port_input *pi, struct sockcred *cred) 256 { 257 258 /* process explicitly sends credentials */ 259 if (cred) 260 pi->priv = (cred->sc_euid == 0); 261 else 262 pi->priv = 0; 263 } 264 265 /* 266 * Input from a datagram socket. 267 * Each receive should return one datagram. 268 */ 269 static ssize_t 270 udp_recv(struct tport *tp, struct port_input *pi) 271 { 272 u_char embuf[1000]; 273 char cbuf[CMSG_SPACE(SOCKCREDSIZE(CMGROUP_MAX)) + 274 CMSG_SPACE(sizeof(struct in_addr))]; 275 struct udp_port *p = (struct udp_port *)tp; 276 struct msghdr msg; 277 struct iovec iov[1]; 278 ssize_t len; 279 struct cmsghdr *cmsg; 280 struct sockcred *cred = NULL; 281 282 if (pi->buf == NULL) { 283 /* no buffer yet - allocate one */ 284 if ((pi->buf = buf_alloc(0)) == NULL) { 285 /* ups - could not get buffer. Read away input 286 * and drop it */ 287 (void)recvfrom(pi->fd, embuf, sizeof(embuf), 288 0, NULL, NULL); 289 /* return error */ 290 return (-1); 291 } 292 pi->buflen = buf_size(0); 293 } 294 295 /* try to get a message */ 296 msg.msg_name = pi->peer; 297 msg.msg_namelen = pi->peerlen; 298 msg.msg_iov = iov; 299 msg.msg_iovlen = 1; 300 memset(cbuf, 0, sizeof(cbuf)); 301 msg.msg_control = cbuf; 302 msg.msg_controllen = sizeof(cbuf); 303 msg.msg_flags = 0; 304 305 iov[0].iov_base = pi->buf; 306 iov[0].iov_len = pi->buflen; 307 308 len = recvmsg(pi->fd, &msg, 0); 309 310 if (len == -1 || len == 0) 311 /* receive error */ 312 return (-1); 313 314 if (msg.msg_flags & MSG_TRUNC) { 315 /* truncated - drop */ 316 snmpd_stats.silentDrops++; 317 snmpd_stats.inTooLong++; 318 return (-1); 319 } 320 321 pi->length = (size_t)len; 322 323 for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; 324 cmsg = CMSG_NXTHDR(&msg, cmsg)) { 325 if (cmsg->cmsg_level == IPPROTO_IP && 326 cmsg->cmsg_type == IP_RECVDSTADDR) 327 memcpy(&p->dstaddr, CMSG_DATA(cmsg), 328 sizeof(struct in_addr)); 329 if (cmsg->cmsg_level == SOL_SOCKET && 330 cmsg->cmsg_type == SCM_CREDS) 331 cred = (struct sockcred *)(void *)CMSG_DATA(cmsg); 332 } 333 334 if (pi->cred) 335 check_priv_dgram(pi, cred); 336 337 return (0); 338 } 339 340 /* 341 * Port table 342 */ 343 int 344 op_snmp_port(struct snmp_context *ctx, struct snmp_value *value, 345 u_int sub, u_int iidx, enum snmp_op op) 346 { 347 asn_subid_t which = value->var.subs[sub-1]; 348 struct udp_port *p; 349 u_int8_t addr[4]; 350 u_int32_t port; 351 352 switch (op) { 353 354 case SNMP_OP_GETNEXT: 355 if ((p = (struct udp_port *)trans_next_port(my_trans, 356 &value->var, sub)) == NULL) 357 return (SNMP_ERR_NOSUCHNAME); 358 index_append(&value->var, sub, &p->tport.index); 359 break; 360 361 case SNMP_OP_GET: 362 if ((p = (struct udp_port *)trans_find_port(my_trans, 363 &value->var, sub)) == NULL) 364 return (SNMP_ERR_NOSUCHNAME); 365 break; 366 367 case SNMP_OP_SET: 368 p = (struct udp_port *)trans_find_port(my_trans, 369 &value->var, sub); 370 ctx->scratch->int1 = (p != NULL); 371 372 if (which != LEAF_begemotSnmpdPortStatus) 373 abort(); 374 if (!TRUTH_OK(value->v.integer)) 375 return (SNMP_ERR_WRONG_VALUE); 376 377 ctx->scratch->int2 = TRUTH_GET(value->v.integer); 378 379 if (ctx->scratch->int2) { 380 /* open an SNMP port */ 381 if (p != NULL) 382 /* already open - do nothing */ 383 return (SNMP_ERR_NOERROR); 384 385 if (index_decode(&value->var, sub, iidx, addr, &port)) 386 return (SNMP_ERR_NO_CREATION); 387 return (udp_open_port(addr, port, &p)); 388 389 } else { 390 /* close SNMP port - do in commit */ 391 } 392 return (SNMP_ERR_NOERROR); 393 394 case SNMP_OP_ROLLBACK: 395 p = (struct udp_port *)trans_find_port(my_trans, 396 &value->var, sub); 397 if (ctx->scratch->int1 == 0) { 398 /* did not exist */ 399 if (ctx->scratch->int2 == 1) { 400 /* created */ 401 if (p != NULL) 402 udp_close_port(&p->tport); 403 } 404 } 405 return (SNMP_ERR_NOERROR); 406 407 case SNMP_OP_COMMIT: 408 p = (struct udp_port *)trans_find_port(my_trans, 409 &value->var, sub); 410 if (ctx->scratch->int1 == 1) { 411 /* did exist */ 412 if (ctx->scratch->int2 == 0) { 413 /* delete */ 414 if (p != NULL) 415 udp_close_port(&p->tport); 416 } 417 } 418 return (SNMP_ERR_NOERROR); 419 420 default: 421 abort(); 422 } 423 424 /* 425 * Come here to fetch the value 426 */ 427 switch (which) { 428 429 case LEAF_begemotSnmpdPortStatus: 430 value->v.integer = 1; 431 break; 432 433 default: 434 abort(); 435 } 436 437 return (SNMP_ERR_NOERROR); 438 } 439