1 /* $OpenBSD: packet.c,v 1.13 2016/12/23 14:53:16 jca Exp $ */ 2 3 /* 4 * Copyright (c) 2006 Michele Marchetto <mydecay@openbeer.it> 5 * Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org> 6 * 7 * Permission to use, copy, modify, and distribute this software for any 8 * purpose with or without fee is hereby granted, provided that the above 9 * copyright notice and this permission notice appear in all copies. 10 * 11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 */ 19 20 #include <sys/types.h> 21 #include <sys/socket.h> 22 #include <sys/uio.h> 23 24 #include <netinet/in.h> 25 #include <netinet/ip.h> 26 #include <netinet/udp.h> 27 #include <arpa/inet.h> 28 29 #include <net/if_dl.h> 30 31 #include <errno.h> 32 #include <event.h> 33 #include <stdlib.h> 34 #include <string.h> 35 36 #include "ripd.h" 37 #include "rip.h" 38 #include "log.h" 39 #include "ripe.h" 40 41 int rip_hdr_sanity_check(struct rip_hdr *); 42 struct iface *find_iface(struct ripd_conf *, unsigned int, struct in_addr); 43 44 int 45 gen_rip_hdr(struct ibuf *buf, u_int8_t command) 46 { 47 struct rip_hdr rip_hdr; 48 49 bzero(&rip_hdr, sizeof(rip_hdr)); 50 rip_hdr.version = RIP_VERSION; 51 rip_hdr.command = command; 52 53 return (ibuf_add(buf, &rip_hdr, sizeof(rip_hdr))); 54 } 55 56 /* send and receive packets */ 57 int 58 send_packet(struct iface *iface, void *pkt, size_t len, struct sockaddr_in *dst) 59 { 60 /* set outgoing interface for multicast traffic */ 61 if (IN_MULTICAST(ntohl(dst->sin_addr.s_addr))) 62 if (if_set_mcast(iface) == -1) { 63 log_warn("send_packet: error setting multicast " 64 "interface, %s", iface->name); 65 return (-1); 66 } 67 68 if (sendto(iface->fd, pkt, len, 0, 69 (struct sockaddr *)dst, sizeof(*dst)) == -1) { 70 log_warn("send_packet: error sending packet on interface %s", 71 iface->name); 72 return (-1); 73 } 74 75 return (0); 76 } 77 78 void 79 recv_packet(int fd, short event, void *bula) 80 { 81 union { 82 struct cmsghdr hdr; 83 char buf[CMSG_SPACE(sizeof(struct sockaddr_dl))]; 84 } cmsgbuf; 85 struct sockaddr_in src; 86 struct iovec iov; 87 struct msghdr msg; 88 struct cmsghdr *cmsg; 89 struct sockaddr_dl *dst = NULL; 90 struct nbr_failed *nbr_failed = NULL; 91 struct ripd_conf *xconf = bula; 92 struct iface *iface; 93 struct rip_hdr *rip_hdr; 94 struct nbr *nbr; 95 u_int8_t *buf; 96 ssize_t r; 97 u_int16_t len, srcport; 98 u_int32_t auth_crypt_num = 0; 99 100 if (event != EV_READ) 101 return; 102 103 /* setup buffer */ 104 buf = pkt_ptr; 105 106 bzero(&msg, sizeof(msg)); 107 108 iov.iov_base = buf; 109 iov.iov_len = IBUF_READ_SIZE; 110 msg.msg_name = &src; 111 msg.msg_namelen = sizeof(src); 112 msg.msg_iov = &iov; 113 msg.msg_iovlen = 1; 114 msg.msg_control = &cmsgbuf.buf; 115 msg.msg_controllen = sizeof(cmsgbuf.buf); 116 117 if ((r = recvmsg(fd, &msg, 0)) == -1) { 118 if (errno != EINTR && errno != EAGAIN) 119 log_debug("recv_packet: read error: %s", 120 strerror(errno)); 121 return; 122 } 123 124 for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; 125 cmsg = CMSG_NXTHDR(&msg, cmsg)) { 126 if (cmsg->cmsg_level == IPPROTO_IP && 127 cmsg->cmsg_type == IP_RECVIF) { 128 dst = (struct sockaddr_dl *)CMSG_DATA(cmsg); 129 break; 130 } 131 } 132 133 if (dst == NULL) 134 return; 135 136 len = (u_int16_t)r; 137 138 /* Check the packet is not from one of the local interfaces */ 139 LIST_FOREACH(iface, &xconf->iface_list, entry) { 140 if (iface->addr.s_addr == src.sin_addr.s_addr) 141 return; 142 } 143 144 /* find a matching interface */ 145 if ((iface = find_iface(xconf, dst->sdl_index, src.sin_addr)) == NULL) { 146 log_debug("recv_packet: cannot find a matching interface"); 147 return; 148 } 149 150 srcport = ntohs(src.sin_port); 151 152 /* RIP header sanity checks */ 153 if (len < RIP_HDR_LEN) { 154 log_debug("recv_packet: bad packet size"); 155 return; 156 } 157 rip_hdr = (struct rip_hdr *)buf; 158 159 if (rip_hdr_sanity_check(rip_hdr) == -1) 160 return; 161 162 nbr = nbr_find_ip(iface, src.sin_addr.s_addr); 163 164 if (nbr == NULL && iface->auth_type == AUTH_CRYPT) 165 nbr_failed = nbr_failed_find(iface, src.sin_addr.s_addr); 166 167 /* switch RIP command */ 168 switch (rip_hdr->command) { 169 case COMMAND_REQUEST: 170 /* Requests don't create a real neighbor, just a temporary 171 * one to build the response. 172 */ 173 if ((msg.msg_flags & MSG_MCAST) == 0 && srcport == RIP_PORT) 174 return; 175 176 if (nbr == NULL) { 177 nbr = nbr_new(src.sin_addr.s_addr, iface); 178 nbr->addr = src.sin_addr; 179 } 180 nbr->port = srcport; 181 nbr_fsm(nbr, NBR_EVT_REQUEST_RCVD); 182 183 buf += RIP_HDR_LEN; 184 len -= RIP_HDR_LEN; 185 186 recv_request(iface, nbr, buf, len); 187 break; 188 case COMMAND_RESPONSE: 189 if (srcport != RIP_PORT) 190 return; 191 192 if (auth_validate(&buf, &len, iface, nbr, nbr_failed, 193 &auth_crypt_num)) { 194 log_warnx("recv_packet: authentication error, " 195 "interface %s", iface->name); 196 return; 197 } 198 199 if (nbr == NULL) { 200 nbr = nbr_new(src.sin_addr.s_addr, iface); 201 if (nbr_failed != NULL) 202 nbr_failed_delete(nbr_failed); 203 nbr->addr = src.sin_addr; 204 } 205 nbr->auth_seq_num = auth_crypt_num; 206 nbr_fsm(nbr, NBR_EVT_RESPONSE_RCVD); 207 208 recv_response(iface, nbr, buf, len); 209 break; 210 default: 211 log_debug("recv_packet: unknown RIP command, interface %s", 212 iface->name); 213 } 214 } 215 216 int 217 rip_hdr_sanity_check(struct rip_hdr *rip_hdr) 218 { 219 if (rip_hdr->version != RIP_VERSION) { 220 log_debug("rip_hdr_sanity_check: invalid RIP version %d", 221 rip_hdr->version); 222 return (-1); 223 } 224 225 return (0); 226 } 227 228 struct iface * 229 find_iface(struct ripd_conf *xconf, unsigned int ifindex, struct in_addr src) 230 { 231 struct iface *iface = NULL; 232 233 /* returned interface needs to be active */ 234 LIST_FOREACH(iface, &xconf->iface_list, entry) { 235 if (ifindex == 0 || ifindex != iface->ifindex) 236 continue; 237 238 if (iface->passive) 239 continue; 240 241 if ((iface->addr.s_addr & iface->mask.s_addr) == 242 (src.s_addr & iface->mask.s_addr)) 243 return (iface); 244 245 if (iface->dst.s_addr && iface->dst.s_addr == src.s_addr) 246 return (iface); 247 } 248 249 return (NULL); 250 } 251