xref: /openbsd-src/usr.sbin/ldpd/hello.c (revision d13be5d47e4149db2549a9828e244d59dbc43f15)
1 /*	$OpenBSD: hello.c,v 1.12 2011/03/12 01:57:13 claudio Exp $ */
2 
3 /*
4  * Copyright (c) 2009 Michele Marchetto <michele@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 #include <sys/uio.h>
22 
23 #include <netinet/in.h>
24 #include <netinet/in_systm.h>
25 #include <netinet/ip.h>
26 #include <arpa/inet.h>
27 #include <net/if_dl.h>
28 
29 #include <errno.h>
30 #include <event.h>
31 #include <stdlib.h>
32 #include <string.h>
33 
34 #include "ldpd.h"
35 #include "ldp.h"
36 #include "log.h"
37 #include "ldpe.h"
38 
39 int	tlv_decode_hello_prms(char *, u_int16_t, u_int16_t *, u_int16_t *);
40 int	tlv_decode_opt_hello_prms(char *, u_int16_t, struct in_addr *,
41 	    u_int32_t *);
42 int	gen_hello_prms_tlv(struct iface *, struct ibuf *, u_int16_t);
43 
44 int
45 send_hello(struct iface *iface)
46 {
47 	struct sockaddr_in	 dst;
48 	struct ibuf		*buf;
49 	u_int16_t		 size;
50 
51 	dst.sin_port = htons(LDP_PORT);
52 	dst.sin_family = AF_INET;
53 	dst.sin_len = sizeof(struct sockaddr_in);
54 	inet_aton(AllRouters, &dst.sin_addr);
55 
56 	if (iface->passive)
57 		return (0);
58 
59 	if ((buf = ibuf_open(LDP_MAX_LEN)) == NULL)
60 		fatal("send_hello");
61 
62 	size = LDP_HDR_SIZE + sizeof(struct ldp_msg) +
63 	    sizeof(struct hello_prms_tlv);
64 
65 	gen_ldp_hdr(buf, iface, size);
66 
67 	size -= LDP_HDR_SIZE;
68 
69 	gen_msg_tlv(buf, MSG_TYPE_HELLO, size);
70 
71 	size -= sizeof(struct ldp_msg);
72 
73 	gen_hello_prms_tlv(iface, buf, size);
74 
75 	send_packet(iface, buf->buf, buf->wpos, &dst);
76 	ibuf_free(buf);
77 
78 	return (0);
79 }
80 
81 void
82 recv_hello(struct iface *iface, struct in_addr src, char *buf, u_int16_t len)
83 {
84 	struct ldp_msg		 hello;
85 	struct ldp_hdr		 ldp;
86 	struct nbr		*nbr = NULL;
87 	struct in_addr		 address;
88 	u_int32_t		 conf_number;
89 	u_int16_t		 holdtime, flags;
90 	int			 r;
91 
92 	bcopy(buf, &ldp, sizeof(ldp));
93 	buf += LDP_HDR_SIZE;
94 	len -= LDP_HDR_SIZE;
95 
96 	bcopy(buf, &hello, sizeof(hello));
97 	buf += sizeof(struct ldp_msg);
98 	len -= sizeof(struct ldp_msg);
99 
100 	r = tlv_decode_hello_prms(buf, len, &holdtime, &flags);
101 	if (r == -1) {
102 		address.s_addr = ldp.lsr_id;
103 		log_debug("recv_hello: neighbor %s: failed to decode params",
104 		    inet_ntoa(address));
105 		return;
106 	}
107 
108 	buf += r;
109 	len -= r;
110 
111 	r = tlv_decode_opt_hello_prms(buf, len, &address, &conf_number);
112 	if (r == -1) {
113 		address.s_addr = ldp.lsr_id;
114 		log_debug("recv_hello: neighbor %s: failed to decode "
115 		    "optional params", inet_ntoa(address));
116 		return;
117 	}
118 	if (r != len) {
119 		address.s_addr = ldp.lsr_id;
120 		log_debug("recv_hello: neighbor %s: unexpected data in message",
121 		    inet_ntoa(address));
122 		return;
123 	}
124 
125 	nbr = nbr_find_ldpid(ldp.lsr_id, ldp.lspace_id);
126 	if (!nbr) {
127 		struct in_addr	a;
128 
129 		if (address.s_addr == INADDR_ANY)
130 			a = src;
131 		else
132 			a = address;
133 
134 		nbr = nbr_new(ldp.lsr_id, ldp.lspace_id, iface, a);
135 
136 		/* set neighbor parameters */
137 		nbr->hello_type = flags;
138 
139 		if (holdtime == 0) {
140 			/* XXX: lacks support for targeted hellos */
141 			if (iface->holdtime < LINK_DFLT_HOLDTIME)
142 				nbr->holdtime = iface->holdtime;
143 			else
144 				nbr->holdtime = LINK_DFLT_HOLDTIME;
145 		} else if (holdtime == INFINITE_HOLDTIME) {
146 			/* No timeout for this neighbor */
147 			nbr->holdtime = iface->holdtime;
148 		} else {
149 			if (iface->holdtime < holdtime)
150 				nbr->holdtime = iface->holdtime;
151 			else
152 				nbr->holdtime = holdtime;
153 		}
154 	}
155 
156 	nbr_fsm(nbr, NBR_EVT_HELLO_RCVD);
157 
158 	if (ntohl(nbr->addr.s_addr) < ntohl(nbr->iface->addr.s_addr) &&
159 	    nbr->state == NBR_STA_PRESENT && !nbr_pending_idtimer(nbr))
160 		nbr_act_session_establish(nbr, 1);
161 }
162 
163 int
164 gen_hello_prms_tlv(struct iface *iface, struct ibuf *buf, u_int16_t size)
165 {
166 	struct hello_prms_tlv	parms;
167 
168 	/* We want just the size of the value */
169 	size -= TLV_HDR_LEN;
170 
171 	bzero(&parms, sizeof(parms));
172 	parms.type = htons(TLV_TYPE_COMMONHELLO);
173 	parms.length = htons(size);
174 	/* XXX */
175 	parms.holdtime = htons(iface->holdtime);
176 	parms.flags = 0;
177 
178 	return (ibuf_add(buf, &parms, sizeof(parms)));
179 }
180 
181 int
182 tlv_decode_hello_prms(char *buf, u_int16_t len, u_int16_t *holdtime,
183     u_int16_t *flags)
184 {
185 	struct hello_prms_tlv	tlv;
186 
187 	if (len < sizeof(tlv))
188 		return (-1);
189 	bcopy(buf, &tlv, sizeof(tlv));
190 
191 	if (ntohs(tlv.length) != sizeof(tlv) - TLV_HDR_LEN)
192 		return (-1);
193 
194 	if (tlv.type != htons(TLV_TYPE_COMMONHELLO))
195 		return (-1);
196 
197 	*holdtime = ntohs(tlv.holdtime);
198 	*flags = ntohs(tlv.flags);
199 
200 	return (sizeof(tlv));
201 }
202 
203 int
204 tlv_decode_opt_hello_prms(char *buf, u_int16_t len, struct in_addr *addr,
205     u_int32_t *conf_number)
206 {
207 	struct tlv	tlv;
208 	int		cons = 0;
209 	u_int16_t	tlv_len;
210 
211 	bzero(addr, sizeof(*addr));
212 	*conf_number = 0;
213 
214 	while (len >= sizeof(tlv)) {
215 		bcopy(buf, &tlv, sizeof(tlv));
216 		tlv_len = ntohs(tlv.length);
217 		switch (ntohs(tlv.type)) {
218 		case TLV_TYPE_IPV4TRANSADDR:
219 			if (tlv_len != sizeof(u_int32_t))
220 				return (-1);
221 			bcopy(buf + TLV_HDR_LEN, addr, sizeof(u_int32_t));
222 			break;
223 		case TLV_TYPE_CONFIG:
224 			if (tlv_len != sizeof(u_int32_t))
225 				return (-1);
226 			bcopy(buf + TLV_HDR_LEN, conf_number,
227 			    sizeof(u_int32_t));
228 			break;
229 		default:
230 			/* if unknown flag set, ignore TLV */
231 			if (!(ntohs(tlv.type) & UNKNOWN_FLAG))
232 				return (-1);
233 			break;
234 		}
235 		buf += TLV_HDR_LEN + tlv_len;
236 		len -= TLV_HDR_LEN + tlv_len;
237 		cons += TLV_HDR_LEN + tlv_len;
238 	}
239 
240 	return (cons);
241 }
242