xref: /openbsd/usr.sbin/ospfd/packet.c (revision 590eae4c)
1 /*	$OpenBSD: packet.c,v 1.19 2006/02/19 18:52:06 norby Exp $ */
2 
3 /*
4  * Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/types.h>
20 #include <sys/socket.h>
21 
22 #include <netinet/in.h>
23 #include <netinet/in_systm.h>
24 #include <netinet/ip.h>
25 #include <arpa/inet.h>
26 
27 #include <errno.h>
28 #include <event.h>
29 #include <stdlib.h>
30 #include <string.h>
31 
32 #include "ospfd.h"
33 #include "ospf.h"
34 #include "log.h"
35 #include "ospfe.h"
36 
37 int		 ip_hdr_sanity_check(const struct ip *, u_int16_t);
38 int		 ospf_hdr_sanity_check(const struct ip *,
39 		    struct ospf_hdr *, u_int16_t, const struct iface *);
40 struct iface	*find_iface(struct ospfd_conf *, struct in_addr);
41 
42 int
43 gen_ospf_hdr(struct buf *buf, struct iface *iface, u_int8_t type)
44 {
45 	struct ospf_hdr	ospf_hdr;
46 
47 	bzero(&ospf_hdr, sizeof(ospf_hdr));
48 	ospf_hdr.version = OSPF_VERSION;
49 	ospf_hdr.type = type;
50 	ospf_hdr.rtr_id = ospfe_router_id();
51 	if (iface->type != IF_TYPE_VIRTUALLINK)
52 		ospf_hdr.area_id = iface->area->id.s_addr;
53 	ospf_hdr.auth_type = htons(iface->auth_type);
54 
55 	return (buf_add(buf, &ospf_hdr, sizeof(ospf_hdr)));
56 }
57 
58 /* send and receive packets */
59 int
60 send_packet(struct iface *iface, char *pkt, int 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 	struct ospfd_conf	*xconf = bula;
84 	struct ip		 ip_hdr;
85 	struct ospf_hdr		*ospf_hdr;
86 	struct iface		*iface;
87 	struct nbr		*nbr = NULL;
88 	struct in_addr		 addr;
89 	char			*buf;
90 	ssize_t			 r;
91 	u_int16_t		 len;
92 	int			 l;
93 
94 	if (event != EV_READ)
95 		return;
96 
97 	/* setup buffer */
98 	buf = pkt_ptr;
99 
100 	if ((r = recvfrom(fd, buf, READ_BUF_SIZE, 0, NULL, NULL)) == -1) {
101 		if (errno != EAGAIN && errno != EINTR)
102 			log_debug("recv_packet: error receiving packet");
103 		return;
104 	}
105 
106 	len = (u_int16_t)r;
107 
108 	/* IP header sanity checks */
109 	if (len < sizeof(ip_hdr)) {
110 		log_warnx("recv_packet: bad packet size");
111 		return;
112 	}
113 	memcpy(&ip_hdr, buf, sizeof(ip_hdr));
114 	if ((l = ip_hdr_sanity_check(&ip_hdr, len)) == -1)
115 		return;
116 	buf += l;
117 	len -= l;
118 
119 	/* find a matching interface */
120 	if ((iface = find_iface(xconf, ip_hdr.ip_src)) == NULL) {
121 		log_debug("recv_packet: cannot find valid interface");
122 		return;
123 	}
124 
125 	/*
126 	 * Packet needs to be sent to AllSPFRouters or AllDRouters
127 	 * or to the address of the interface itself.
128 	 * AllDRouters is only valid for DR and BDR but this is checked later.
129 	 */
130 	inet_aton(AllSPFRouters, &addr);
131 	if (ip_hdr.ip_dst.s_addr != addr.s_addr) {
132 		inet_aton(AllDRouters, &addr);
133 		if (ip_hdr.ip_dst.s_addr != addr.s_addr) {
134 			if (ip_hdr.ip_dst.s_addr != iface->addr.s_addr) {
135 				log_debug("recv_packet: packet sent to wrong "
136 				    "address %s, interface %s",
137 				    inet_ntoa(ip_hdr.ip_dst), iface->name);
138 				return;
139 			}
140 		}
141 	}
142 
143 	/* OSPF header sanity checks */
144 	if (len < sizeof(*ospf_hdr)) {
145 		log_warnx("recv_packet: bad packet size");
146 		return;
147 	}
148 	ospf_hdr = (struct ospf_hdr *)buf;
149 
150 	if ((l = ospf_hdr_sanity_check(&ip_hdr, ospf_hdr, len, iface)) == -1)
151 		return;
152 
153 	nbr = nbr_find_id(iface, ospf_hdr->rtr_id);
154 	if (ospf_hdr->type != PACKET_TYPE_HELLO && nbr == NULL) {
155 		log_debug("recv_packet: unknown neighbor ID");
156 		return;
157 	}
158 
159 	if (auth_validate(buf, len, iface, nbr)) {
160 		log_warnx("recv_packet: authentication error, "
161 		    "interface %s", iface->name);
162 		return;
163 	}
164 
165 	buf += sizeof(*ospf_hdr);
166 	len = l - sizeof(*ospf_hdr);
167 
168 	/* switch OSPF packet type */
169 	switch (ospf_hdr->type) {
170 	case PACKET_TYPE_HELLO:
171 		inet_aton(AllDRouters, &addr);
172 		if (ip_hdr.ip_dst.s_addr == addr.s_addr) {
173 			log_debug("recv_packet: invalid destination IP "
174 			     "address");
175 			break;
176 		}
177 
178 		recv_hello(iface, ip_hdr.ip_src, ospf_hdr->rtr_id, buf, len);
179 		break;
180 	case PACKET_TYPE_DD:
181 		recv_db_description(nbr, buf, len);
182 		break;
183 	case PACKET_TYPE_LS_REQUEST:
184 		recv_ls_req(nbr, buf, len);
185 		break;
186 	case PACKET_TYPE_LS_UPDATE:
187 		recv_ls_update(nbr, buf, len);
188 		break;
189 	case PACKET_TYPE_LS_ACK:
190 		recv_ls_ack(nbr, buf, len);
191 		break;
192 	default:
193 		log_debug("recv_packet: unknown OSPF packet type, interface %s",
194 		    iface->name);
195 	}
196 }
197 
198 int
199 ip_hdr_sanity_check(const struct ip *ip_hdr, u_int16_t len)
200 {
201 	if (ntohs(ip_hdr->ip_len) != len) {
202 		log_debug("recv_packet: invalid IP packet length %u",
203 		    ntohs(ip_hdr->ip_len));
204 		return (-1);
205 	}
206 
207 	if (ip_hdr->ip_p != IPPROTO_OSPF)
208 		/* this is enforced by the socket itself */
209 		fatalx("recv_packet: invalid IP proto");
210 
211 	return (ip_hdr->ip_hl << 2);
212 }
213 
214 int
215 ospf_hdr_sanity_check(const struct ip *ip_hdr, struct ospf_hdr *ospf_hdr,
216     u_int16_t len, const struct iface *iface)
217 {
218 	struct in_addr		 addr;
219 
220 	if (ospf_hdr->version != OSPF_VERSION) {
221 		log_debug("recv_packet: invalid OSPF version %d",
222 		    ospf_hdr->version);
223 		return (-1);
224 	}
225 
226 	if (ntohs(ospf_hdr->len) > len ||
227 	    len <= sizeof(struct ospf_hdr)) {
228 		log_debug("recv_packet: invalid OSPF packet length %d",
229 		    ntohs(ospf_hdr->len));
230 		return (-1);
231 	}
232 
233 	if (iface->type != IF_TYPE_VIRTUALLINK) {
234 		if (ospf_hdr->area_id != iface->area->id.s_addr) {
235 			addr.s_addr = ospf_hdr->area_id;
236 			log_debug("recv_packet: invalid area ID %s, "
237 			    "interface %s", inet_ntoa(addr), iface->name);
238 			return (-1);
239 		}
240 	} else {
241 		if (ospf_hdr->area_id != 0) {
242 			addr.s_addr = ospf_hdr->area_id;
243 			log_debug("recv_packet: invalid area ID %s, "
244 			    "interface %s", inet_ntoa(addr), iface->name);
245 			return (-1);
246 		}
247 	}
248 
249 	if (iface->type == IF_TYPE_BROADCAST || iface->type == IF_TYPE_NBMA) {
250 		if (inet_aton(AllDRouters, &addr) == 0)
251 			fatalx("recv_packet: inet_aton");
252 		if (ip_hdr->ip_dst.s_addr == addr.s_addr &&
253 		    (iface->state & IF_STA_DRORBDR) == 0) {
254 			log_debug("recv_packet: invalid destination IP in "
255 			    "state %s, interface %s",
256 			    if_state_name(iface->state), iface->name);
257 			return (-1);
258 		}
259 	}
260 
261 	return (ntohs(ospf_hdr->len));
262 }
263 
264 struct iface *
265 find_iface(struct ospfd_conf *xconf, struct in_addr src)
266 {
267 	struct area	*area = NULL;
268 	struct iface	*iface = NULL;
269 
270 	/* returned interface needs to be active */
271 	LIST_FOREACH(area, &xconf->area_list, entry) {
272 		LIST_FOREACH(iface, &area->iface_list, entry) {
273 			if (iface->fd > 0 &&
274 			    (iface->type == IF_TYPE_POINTOPOINT) &&
275 			    (iface->dst.s_addr == src.s_addr) &&
276 			    !iface->passive)
277 				return (iface);
278 
279 			if (iface->fd > 0 && (iface->addr.s_addr &
280 			    iface->mask.s_addr) == (src.s_addr &
281 			    iface->mask.s_addr) && !iface->passive &&
282 			    iface->type != IF_TYPE_VIRTUALLINK) {
283 				return (iface);
284 			}
285 		}
286 	}
287 
288 	LIST_FOREACH(area, &xconf->area_list, entry)
289 		LIST_FOREACH(iface, &area->iface_list, entry)
290 			if ((iface->type == IF_TYPE_VIRTUALLINK) &&
291 			    (src.s_addr == iface->dst.s_addr)) {
292 				return (iface);
293 			}
294 
295 	return (NULL);
296 }
297