1 /* $OpenBSD: packet.c,v 1.7 2007/10/24 20:52:50 claudio 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 char cbuf[CMSG_SPACE(sizeof(struct sockaddr_dl))]; 83 struct sockaddr_in src; 84 struct iovec iov; 85 struct msghdr msg; 86 struct cmsghdr *cmsg; 87 struct sockaddr_dl *dst = NULL; 88 struct nbr_failed *nbr_failed = NULL; 89 struct ripd_conf *xconf = bula; 90 struct iface *iface; 91 struct rip_hdr *rip_hdr; 92 struct nbr *nbr; 93 u_int8_t *buf; 94 ssize_t r; 95 u_int16_t len, srcport; 96 u_int32_t auth_crypt_num = 0; 97 98 if (event != EV_READ) 99 return; 100 101 /* setup buffer */ 102 buf = pkt_ptr; 103 104 bzero(&msg, sizeof(msg)); 105 106 iov.iov_base = buf; 107 iov.iov_len = READ_BUF_SIZE; 108 msg.msg_name = &src; 109 msg.msg_namelen = sizeof(src); 110 msg.msg_iov = &iov; 111 msg.msg_iovlen = 1; 112 msg.msg_control = cbuf; 113 msg.msg_controllen = sizeof(cbuf); 114 115 if ((r = recvmsg(fd, &msg, 0)) == -1) { 116 if (errno != EINTR && errno != EAGAIN) 117 log_debug("recv_packet: read error: %s", 118 strerror(errno)); 119 return; 120 } 121 122 for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; 123 cmsg = CMSG_NXTHDR(&msg, cmsg)) { 124 if (cmsg->cmsg_level == IPPROTO_IP && 125 cmsg->cmsg_type == IP_RECVIF) { 126 dst = (struct sockaddr_dl *)CMSG_DATA(cmsg); 127 break; 128 } 129 } 130 131 if (dst == NULL) 132 return; 133 134 len = (u_int16_t)r; 135 136 /* Check the packet is not from one of the local interfaces */ 137 LIST_FOREACH(iface, &xconf->iface_list, entry) { 138 if (iface->addr.s_addr == src.sin_addr.s_addr) 139 return; 140 } 141 142 /* find a matching interface */ 143 if ((iface = find_iface(xconf, dst->sdl_index, src.sin_addr)) == NULL) { 144 log_debug("recv_packet: cannot find a matching interface"); 145 return; 146 } 147 148 srcport = ntohs(src.sin_port); 149 150 /* RIP header sanity checks */ 151 if (len < RIP_HDR_LEN) { 152 log_debug("recv_packet: bad packet size"); 153 return; 154 } 155 rip_hdr = (struct rip_hdr *)buf; 156 157 if (rip_hdr_sanity_check(rip_hdr) == -1) 158 return; 159 160 nbr = nbr_find_ip(iface, src.sin_addr.s_addr); 161 162 if (nbr == NULL && iface->auth_type == AUTH_CRYPT) 163 nbr_failed = nbr_failed_find(iface, src.sin_addr.s_addr); 164 165 /* switch RIP command */ 166 switch (rip_hdr->command) { 167 case COMMAND_REQUEST: 168 /* Requests don't create a real neighbor, just a temporary 169 * one to build the response. 170 */ 171 if ((msg.msg_flags & MSG_MCAST) == 0 && srcport == RIP_PORT) 172 return; 173 174 if (nbr == NULL) { 175 nbr = nbr_new(src.sin_addr.s_addr, iface); 176 nbr->addr = src.sin_addr; 177 } 178 nbr->port = srcport; 179 nbr_fsm(nbr, NBR_EVT_REQUEST_RCVD); 180 181 buf += RIP_HDR_LEN; 182 len -= RIP_HDR_LEN; 183 184 recv_request(iface, nbr, buf, len); 185 break; 186 case COMMAND_RESPONSE: 187 if (srcport != RIP_PORT) 188 return; 189 190 if (auth_validate(&buf, &len, iface, nbr, nbr_failed, 191 &auth_crypt_num)) { 192 log_warnx("recv_packet: authentication error, " 193 "interface %s", iface->name); 194 return; 195 } 196 197 if (nbr == NULL) { 198 nbr = nbr_new(src.sin_addr.s_addr, iface); 199 if (nbr_failed != NULL) 200 nbr_failed_delete(nbr_failed); 201 nbr->addr = src.sin_addr; 202 } 203 nbr->auth_seq_num = auth_crypt_num; 204 nbr_fsm(nbr, NBR_EVT_RESPONSE_RCVD); 205 206 recv_response(iface, nbr, buf, len); 207 break; 208 default: 209 log_debug("recv_packet: unknown RIP command, interface %s", 210 iface->name); 211 } 212 } 213 214 int 215 rip_hdr_sanity_check(struct rip_hdr *rip_hdr) 216 { 217 if (rip_hdr->version != RIP_VERSION) { 218 log_debug("rip_hdr_sanity_check: invalid RIP version %d", 219 rip_hdr->version); 220 return (-1); 221 } 222 223 return (0); 224 } 225 226 struct iface * 227 find_iface(struct ripd_conf *xconf, unsigned int ifindex, struct in_addr src) 228 { 229 struct iface *iface = NULL; 230 231 /* returned interface needs to be active */ 232 LIST_FOREACH(iface, &xconf->iface_list, entry) { 233 if (ifindex != 0 && ifindex == iface->ifindex && 234 !iface->passive && (iface->addr.s_addr & 235 iface->mask.s_addr) == (src.s_addr & iface->mask.s_addr)) 236 /* 237 * XXX may fail on P2P links because src and dst don't 238 * have to share a common subnet on the otherhand 239 * checking something like this will help to support 240 * multiple networks configured on one interface. 241 */ 242 return (iface); 243 } 244 245 return (NULL); 246 } 247