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