1 /* $OpenBSD: packet.c,v 1.10 2008/03/24 16:11:05 deraadt 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/in_systm.h> 26 #include <netinet/ip.h> 27 #include <netinet/udp.h> 28 #include <arpa/inet.h> 29 30 #include <net/if_dl.h> 31 32 #include <errno.h> 33 #include <event.h> 34 #include <stdlib.h> 35 #include <string.h> 36 37 #include "ripd.h" 38 #include "rip.h" 39 #include "log.h" 40 #include "ripe.h" 41 42 int rip_hdr_sanity_check(struct rip_hdr *); 43 struct iface *find_iface(struct ripd_conf *, unsigned int, struct in_addr); 44 45 int 46 gen_rip_hdr(struct buf *buf, u_int8_t command) 47 { 48 struct rip_hdr rip_hdr; 49 50 bzero(&rip_hdr, sizeof(rip_hdr)); 51 rip_hdr.version = RIP_VERSION; 52 rip_hdr.command = command; 53 54 return (buf_add(buf, &rip_hdr, sizeof(rip_hdr))); 55 } 56 57 /* send and receive packets */ 58 int 59 send_packet(struct iface *iface, void *pkt, size_t len, struct sockaddr_in *dst) 60 { 61 /* set outgoing interface for multicast traffic */ 62 if (IN_MULTICAST(ntohl(dst->sin_addr.s_addr))) 63 if (if_set_mcast(iface) == -1) { 64 log_warn("send_packet: error setting multicast " 65 "interface, %s", iface->name); 66 return (-1); 67 } 68 69 if (sendto(iface->fd, pkt, len, 0, 70 (struct sockaddr *)dst, sizeof(*dst)) == -1) { 71 log_warn("send_packet: error sending packet on interface %s", 72 iface->name); 73 return (-1); 74 } 75 76 return (0); 77 } 78 79 void 80 recv_packet(int fd, short event, void *bula) 81 { 82 union { 83 struct cmsghdr hdr; 84 char buf[CMSG_SPACE(sizeof(struct sockaddr_dl))]; 85 } cmsgbuf; 86 struct sockaddr_in src; 87 struct iovec iov; 88 struct msghdr msg; 89 struct cmsghdr *cmsg; 90 struct sockaddr_dl *dst = NULL; 91 struct nbr_failed *nbr_failed = NULL; 92 struct ripd_conf *xconf = bula; 93 struct iface *iface; 94 struct rip_hdr *rip_hdr; 95 struct nbr *nbr; 96 u_int8_t *buf; 97 ssize_t r; 98 u_int16_t len, srcport; 99 u_int32_t auth_crypt_num = 0; 100 101 if (event != EV_READ) 102 return; 103 104 /* setup buffer */ 105 buf = pkt_ptr; 106 107 bzero(&msg, sizeof(msg)); 108 109 iov.iov_base = buf; 110 iov.iov_len = READ_BUF_SIZE; 111 msg.msg_name = &src; 112 msg.msg_namelen = sizeof(src); 113 msg.msg_iov = &iov; 114 msg.msg_iovlen = 1; 115 msg.msg_control = &cmsgbuf.buf; 116 msg.msg_controllen = sizeof(cmsgbuf.buf); 117 118 if ((r = recvmsg(fd, &msg, 0)) == -1) { 119 if (errno != EINTR && errno != EAGAIN) 120 log_debug("recv_packet: read error: %s", 121 strerror(errno)); 122 return; 123 } 124 125 for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; 126 cmsg = CMSG_NXTHDR(&msg, cmsg)) { 127 if (cmsg->cmsg_level == IPPROTO_IP && 128 cmsg->cmsg_type == IP_RECVIF) { 129 dst = (struct sockaddr_dl *)CMSG_DATA(cmsg); 130 break; 131 } 132 } 133 134 if (dst == NULL) 135 return; 136 137 len = (u_int16_t)r; 138 139 /* Check the packet is not from one of the local interfaces */ 140 LIST_FOREACH(iface, &xconf->iface_list, entry) { 141 if (iface->addr.s_addr == src.sin_addr.s_addr) 142 return; 143 } 144 145 /* find a matching interface */ 146 if ((iface = find_iface(xconf, dst->sdl_index, src.sin_addr)) == NULL) { 147 log_debug("recv_packet: cannot find a matching interface"); 148 return; 149 } 150 151 srcport = ntohs(src.sin_port); 152 153 /* RIP header sanity checks */ 154 if (len < RIP_HDR_LEN) { 155 log_debug("recv_packet: bad packet size"); 156 return; 157 } 158 rip_hdr = (struct rip_hdr *)buf; 159 160 if (rip_hdr_sanity_check(rip_hdr) == -1) 161 return; 162 163 nbr = nbr_find_ip(iface, src.sin_addr.s_addr); 164 165 if (nbr == NULL && iface->auth_type == AUTH_CRYPT) 166 nbr_failed = nbr_failed_find(iface, src.sin_addr.s_addr); 167 168 /* switch RIP command */ 169 switch (rip_hdr->command) { 170 case COMMAND_REQUEST: 171 /* Requests don't create a real neighbor, just a temporary 172 * one to build the response. 173 */ 174 if ((msg.msg_flags & MSG_MCAST) == 0 && srcport == RIP_PORT) 175 return; 176 177 if (nbr == NULL) { 178 nbr = nbr_new(src.sin_addr.s_addr, iface); 179 nbr->addr = src.sin_addr; 180 } 181 nbr->port = srcport; 182 nbr_fsm(nbr, NBR_EVT_REQUEST_RCVD); 183 184 buf += RIP_HDR_LEN; 185 len -= RIP_HDR_LEN; 186 187 recv_request(iface, nbr, buf, len); 188 break; 189 case COMMAND_RESPONSE: 190 if (srcport != RIP_PORT) 191 return; 192 193 if (auth_validate(&buf, &len, iface, nbr, nbr_failed, 194 &auth_crypt_num)) { 195 log_warnx("recv_packet: authentication error, " 196 "interface %s", iface->name); 197 return; 198 } 199 200 if (nbr == NULL) { 201 nbr = nbr_new(src.sin_addr.s_addr, iface); 202 if (nbr_failed != NULL) 203 nbr_failed_delete(nbr_failed); 204 nbr->addr = src.sin_addr; 205 } 206 nbr->auth_seq_num = auth_crypt_num; 207 nbr_fsm(nbr, NBR_EVT_RESPONSE_RCVD); 208 209 recv_response(iface, nbr, buf, len); 210 break; 211 default: 212 log_debug("recv_packet: unknown RIP command, interface %s", 213 iface->name); 214 } 215 } 216 217 int 218 rip_hdr_sanity_check(struct rip_hdr *rip_hdr) 219 { 220 if (rip_hdr->version != RIP_VERSION) { 221 log_debug("rip_hdr_sanity_check: invalid RIP version %d", 222 rip_hdr->version); 223 return (-1); 224 } 225 226 return (0); 227 } 228 229 struct iface * 230 find_iface(struct ripd_conf *xconf, unsigned int ifindex, struct in_addr src) 231 { 232 struct iface *iface = NULL; 233 234 /* returned interface needs to be active */ 235 LIST_FOREACH(iface, &xconf->iface_list, entry) { 236 if (ifindex != 0 && ifindex == iface->ifindex && 237 !iface->passive && (iface->addr.s_addr & 238 iface->mask.s_addr) == (src.s_addr & iface->mask.s_addr)) 239 /* 240 * XXX may fail on P2P links because src and dst don't 241 * have to share a common subnet on the otherhand 242 * checking something like this will help to support 243 * multiple networks configured on one interface. 244 */ 245 return (iface); 246 } 247 248 return (NULL); 249 } 250