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 of this software and documentation and use in source and 9 * binary forms, with or without modification, are permitted provided that 10 * the following conditions are met: 11 * 12 * 1. Redistributions of source code or documentation must retain the above 13 * copyright notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 3. Neither the name of the Institute nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY FRAUNHOFER FOKUS 22 * AND ITS CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, 23 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 24 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 25 * FRAUNHOFER FOKUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 27 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 28 * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 29 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 30 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 31 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 * 33 * $Begemot: bsnmp/snmpd/trans_udp.c,v 1.3 2003/12/09 12:28:53 hbb Exp $ 34 * 35 * UDP transport 36 */ 37 #include <sys/types.h> 38 39 #include <stdlib.h> 40 #include <syslog.h> 41 #include <string.h> 42 #include <errno.h> 43 #include <unistd.h> 44 45 #include <netinet/in.h> 46 #include <arpa/inet.h> 47 48 #include "snmpmod.h" 49 #include "snmpd.h" 50 #include "trans_udp.h" 51 #include "tree.h" 52 #include "oid.h" 53 54 static int udp_start(void); 55 static int udp_stop(int); 56 static void udp_close_port(struct tport *); 57 static int udp_init_port(struct tport *); 58 static ssize_t udp_send(struct tport *, const u_char *, size_t, 59 const struct sockaddr *, size_t); 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 }; 71 static struct transport *my_trans; 72 73 static int 74 udp_start(void) 75 { 76 return (trans_register(&udp_trans, &my_trans)); 77 } 78 79 static int 80 udp_stop(int force __unused) 81 { 82 if (my_trans != NULL) 83 if (trans_unregister(my_trans) != 0) 84 return (SNMP_ERR_GENERR); 85 return (SNMP_ERR_NOERROR); 86 } 87 88 /* 89 * A UDP port is ready 90 */ 91 static void 92 udp_input(int fd __unused, void *udata) 93 { 94 struct udp_port *p = udata; 95 96 p->input.peerlen = sizeof(p->ret); 97 snmpd_input(&p->input, &p->tport); 98 } 99 100 /* 101 * Create a UDP socket and bind it to the given port 102 */ 103 static int 104 udp_init_port(struct tport *tp) 105 { 106 struct udp_port *p = (struct udp_port *)tp; 107 struct sockaddr_in addr; 108 u_int32_t ip; 109 110 if ((p->input.fd = socket(PF_INET, SOCK_DGRAM, 0)) < 0) { 111 syslog(LOG_ERR, "creating UDP socket: %m"); 112 return (SNMP_ERR_RES_UNAVAIL); 113 } 114 ip = (p->addr[0] << 24) | (p->addr[1] << 16) | (p->addr[2] << 8) | 115 p->addr[3]; 116 memset(&addr, 0, sizeof(addr)); 117 addr.sin_addr.s_addr = htonl(ip); 118 addr.sin_port = htons(p->port); 119 addr.sin_family = AF_INET; 120 addr.sin_len = sizeof(addr); 121 if (bind(p->input.fd, (struct sockaddr *)&addr, sizeof(addr))) { 122 if (errno == EADDRNOTAVAIL) { 123 close(p->input.fd); 124 p->input.fd = -1; 125 return (SNMP_ERR_INCONS_NAME); 126 } 127 syslog(LOG_ERR, "bind: %s:%u %m", inet_ntoa(addr.sin_addr), 128 p->port); 129 close(p->input.fd); 130 p->input.fd = -1; 131 return (SNMP_ERR_GENERR); 132 } 133 if ((p->input.id = fd_select(p->input.fd, udp_input, 134 p, NULL)) == NULL) { 135 close(p->input.fd); 136 p->input.fd = -1; 137 return (SNMP_ERR_GENERR); 138 } 139 return (SNMP_ERR_NOERROR); 140 } 141 142 /* 143 * Create a new SNMP Port object and start it, if we are not 144 * in initialisation mode. The arguments are in host byte order. 145 */ 146 static int 147 udp_open_port(u_int8_t *addr, u_int32_t udp_port, struct udp_port **pp) 148 { 149 struct udp_port *port; 150 int err; 151 152 if (udp_port > 0xffff) 153 return (SNMP_ERR_NO_CREATION); 154 if ((port = malloc(sizeof(*port))) == NULL) 155 return (SNMP_ERR_GENERR); 156 memset(port, 0, sizeof(*port)); 157 158 /* initialize common part */ 159 port->tport.index.len = 5; 160 port->tport.index.subs[0] = addr[0]; 161 port->tport.index.subs[1] = addr[1]; 162 port->tport.index.subs[2] = addr[2]; 163 port->tport.index.subs[3] = addr[3]; 164 port->tport.index.subs[4] = udp_port; 165 166 port->addr[0] = addr[0]; 167 port->addr[1] = addr[1]; 168 port->addr[2] = addr[2]; 169 port->addr[3] = addr[3]; 170 port->port = udp_port; 171 172 port->input.fd = -1; 173 port->input.id = NULL; 174 port->input.stream = 0; 175 port->input.cred = 0; 176 port->input.peer = (struct sockaddr *)&port->ret; 177 port->input.peerlen = sizeof(port->ret); 178 179 trans_insert_port(my_trans, &port->tport); 180 181 if (community != COMM_INITIALIZE && 182 (err = udp_init_port(&port->tport)) != SNMP_ERR_NOERROR) { 183 udp_close_port(&port->tport); 184 return (err); 185 } 186 *pp = port; 187 return (SNMP_ERR_NOERROR); 188 } 189 190 /* 191 * Close an SNMP port 192 */ 193 static void 194 udp_close_port(struct tport *tp) 195 { 196 struct udp_port *port = (struct udp_port *)tp; 197 198 snmpd_input_close(&port->input); 199 trans_remove_port(tp); 200 free(port); 201 } 202 203 /* 204 * Send something 205 */ 206 static ssize_t 207 udp_send(struct tport *tp, const u_char *buf, size_t len, 208 const struct sockaddr *addr, size_t addrlen) 209 { 210 struct udp_port *p = (struct udp_port *)tp; 211 212 return (sendto(p->input.fd, buf, len, 0, addr, addrlen)); 213 } 214 215 /* 216 * Port table 217 */ 218 int 219 op_snmp_port(struct snmp_context *ctx, struct snmp_value *value, 220 u_int sub, u_int iidx, enum snmp_op op) 221 { 222 asn_subid_t which = value->var.subs[sub-1]; 223 struct udp_port *p; 224 u_int8_t addr[4]; 225 u_int32_t port; 226 227 switch (op) { 228 229 case SNMP_OP_GETNEXT: 230 if ((p = (struct udp_port *)trans_next_port(my_trans, 231 &value->var, sub)) == NULL) 232 return (SNMP_ERR_NOSUCHNAME); 233 index_append(&value->var, sub, &p->tport.index); 234 break; 235 236 case SNMP_OP_GET: 237 if ((p = (struct udp_port *)trans_find_port(my_trans, 238 &value->var, sub)) == NULL) 239 return (SNMP_ERR_NOSUCHNAME); 240 break; 241 242 case SNMP_OP_SET: 243 p = (struct udp_port *)trans_find_port(my_trans, 244 &value->var, sub); 245 ctx->scratch->int1 = (p != NULL); 246 247 if (which != LEAF_begemotSnmpdPortStatus) 248 abort(); 249 if (!TRUTH_OK(value->v.integer)) 250 return (SNMP_ERR_WRONG_VALUE); 251 252 ctx->scratch->int2 = TRUTH_GET(value->v.integer); 253 254 if (ctx->scratch->int2) { 255 /* open an SNMP port */ 256 if (p != NULL) 257 /* already open - do nothing */ 258 return (SNMP_ERR_NOERROR); 259 260 if (index_decode(&value->var, sub, iidx, addr, &port)) 261 return (SNMP_ERR_NO_CREATION); 262 return (udp_open_port(addr, port, &p)); 263 264 } else { 265 /* close SNMP port - do in commit */ 266 } 267 return (SNMP_ERR_NOERROR); 268 269 case SNMP_OP_ROLLBACK: 270 p = (struct udp_port *)trans_find_port(my_trans, 271 &value->var, sub); 272 if (ctx->scratch->int1 == 0) { 273 /* did not exist */ 274 if (ctx->scratch->int2 == 1) { 275 /* created */ 276 if (p != NULL) 277 udp_close_port(&p->tport); 278 } 279 } 280 return (SNMP_ERR_NOERROR); 281 282 case SNMP_OP_COMMIT: 283 p = (struct udp_port *)trans_find_port(my_trans, 284 &value->var, sub); 285 if (ctx->scratch->int1 == 1) { 286 /* did exist */ 287 if (ctx->scratch->int2 == 0) { 288 /* delete */ 289 if (p != NULL) 290 udp_close_port(&p->tport); 291 } 292 } 293 return (SNMP_ERR_NOERROR); 294 295 default: 296 abort(); 297 } 298 299 /* 300 * Come here to fetch the value 301 */ 302 switch (which) { 303 304 case LEAF_begemotSnmpdPortStatus: 305 value->v.integer = 1; 306 break; 307 308 default: 309 abort(); 310 } 311 312 return (SNMP_ERR_NOERROR); 313 } 314