1*8bc5e3a3Sclaudio /* $OpenBSD: hello.c,v 1.59 2023/07/03 11:51:27 claudio Exp $ */
2ab0c2486Smichele
3ab0c2486Smichele /*
45dc9330aSrenato * Copyright (c) 2013, 2016 Renato Westphal <renato@openbsd.org>
5ab0c2486Smichele * Copyright (c) 2009 Michele Marchetto <michele@openbsd.org>
6ab0c2486Smichele *
7ab0c2486Smichele * Permission to use, copy, modify, and distribute this software for any
8ab0c2486Smichele * purpose with or without fee is hereby granted, provided that the above
9ab0c2486Smichele * copyright notice and this permission notice appear in all copies.
10ab0c2486Smichele *
11ab0c2486Smichele * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12ab0c2486Smichele * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13ab0c2486Smichele * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14ab0c2486Smichele * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15ab0c2486Smichele * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16ab0c2486Smichele * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17ab0c2486Smichele * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18ab0c2486Smichele */
19ab0c2486Smichele
20ab0c2486Smichele #include <sys/types.h>
21ab0c2486Smichele #include <arpa/inet.h>
22ab0c2486Smichele #include <string.h>
23ab0c2486Smichele
24ab0c2486Smichele #include "ldpd.h"
25ab0c2486Smichele #include "ldpe.h"
265411bbb6Srenato #include "log.h"
27ab0c2486Smichele
28c28a25a1Srenato static int gen_hello_prms_tlv(struct ibuf *buf, uint16_t, uint16_t);
29c28a25a1Srenato static int gen_opt4_hello_prms_tlv(struct ibuf *, uint16_t, uint32_t);
30c28a25a1Srenato static int gen_opt16_hello_prms_tlv(struct ibuf *, uint16_t, uint8_t *);
31c28a25a1Srenato static int gen_ds_hello_prms_tlv(struct ibuf *, uint32_t);
32c28a25a1Srenato static int tlv_decode_hello_prms(char *, uint16_t, uint16_t *, uint16_t *);
33c28a25a1Srenato static int tlv_decode_opt_hello_prms(char *, uint16_t, int *, int,
34a8c39dc0Srenato union ldpd_addr *, uint32_t *, uint16_t *);
35ab0c2486Smichele
36ab0c2486Smichele int
send_hello(enum hello_type type,struct iface_af * ia,struct tnbr * tnbr)37a8c39dc0Srenato send_hello(enum hello_type type, struct iface_af *ia, struct tnbr *tnbr)
38ab0c2486Smichele {
39a8c39dc0Srenato int af;
40a8c39dc0Srenato union ldpd_addr dst;
413de94509Srenato uint16_t size, holdtime = 0, flags = 0;
4283dcf737Sclaudio int fd = 0;
43a8c39dc0Srenato struct ibuf *buf;
4465f1d9c1Srenato int err = 0;
4583dcf737Sclaudio
4683dcf737Sclaudio switch (type) {
4783dcf737Sclaudio case HELLO_LINK:
48a8c39dc0Srenato af = ia->af;
49a8c39dc0Srenato holdtime = ia->hello_holdtime;
5083dcf737Sclaudio flags = 0;
51a8c39dc0Srenato fd = (ldp_af_global_get(&global, af))->ldp_disc_socket;
52a8c39dc0Srenato
53a8c39dc0Srenato /* multicast destination address */
54a8c39dc0Srenato switch (af) {
55a8c39dc0Srenato case AF_INET:
565ff72af8Srenato if (!(leconf->ipv4.flags & F_LDPD_AF_NO_GTSM))
57d7525efdSrenato flags |= F_HELLO_GTSM;
58a8c39dc0Srenato dst.v4 = global.mcast_addr_v4;
59a8c39dc0Srenato break;
60a8c39dc0Srenato case AF_INET6:
61a8c39dc0Srenato dst.v6 = global.mcast_addr_v6;
62a8c39dc0Srenato break;
63a8c39dc0Srenato default:
64a8c39dc0Srenato fatalx("send_hello: unknown af");
65a8c39dc0Srenato }
6683dcf737Sclaudio break;
6783dcf737Sclaudio case HELLO_TARGETED:
68a8c39dc0Srenato af = tnbr->af;
6983dcf737Sclaudio holdtime = tnbr->hello_holdtime;
70d7525efdSrenato flags = F_HELLO_TARGETED;
716399cec1Srenato if ((tnbr->flags & F_TNBR_CONFIGURED) || tnbr->pw_count)
72d7525efdSrenato flags |= F_HELLO_REQ_TARG;
73a8c39dc0Srenato fd = (ldp_af_global_get(&global, af))->ldp_edisc_socket;
74a8c39dc0Srenato
75a8c39dc0Srenato /* unicast destination address */
76a8c39dc0Srenato dst = tnbr->addr;
7783dcf737Sclaudio break;
78a8c39dc0Srenato default:
79a8c39dc0Srenato fatalx("send_hello: unknown hello type");
8083dcf737Sclaudio }
81ab0c2486Smichele
82a8c39dc0Srenato /* calculate message size */
83a8c39dc0Srenato size = LDP_HDR_SIZE + LDP_MSG_SIZE + sizeof(struct hello_prms_tlv);
84a8c39dc0Srenato switch (af) {
85a8c39dc0Srenato case AF_INET:
86a8c39dc0Srenato size += sizeof(struct hello_prms_opt4_tlv);
87a8c39dc0Srenato break;
88a8c39dc0Srenato case AF_INET6:
89a8c39dc0Srenato size += sizeof(struct hello_prms_opt16_tlv);
90a8c39dc0Srenato break;
91a8c39dc0Srenato default:
92a8c39dc0Srenato fatalx("send_hello: unknown af");
93a8c39dc0Srenato }
940e35860dSrenato size += sizeof(struct hello_prms_opt4_tlv);
95a8c39dc0Srenato if (ldp_is_dual_stack(leconf))
96a8c39dc0Srenato size += sizeof(struct hello_prms_opt4_tlv);
97ab0c2486Smichele
989277622bSrenato /* generate message */
999277622bSrenato if ((buf = ibuf_open(size)) == NULL)
1009277622bSrenato fatal(__func__);
1019277622bSrenato
10265f1d9c1Srenato err |= gen_ldp_hdr(buf, size);
103ab0c2486Smichele size -= LDP_HDR_SIZE;
10465f1d9c1Srenato err |= gen_msg_hdr(buf, MSG_TYPE_HELLO, size);
10565f1d9c1Srenato err |= gen_hello_prms_tlv(buf, holdtime, flags);
106ab0c2486Smichele
107a8c39dc0Srenato /*
108a8c39dc0Srenato * RFC 7552 - Section 6.1:
109a8c39dc0Srenato * "An LSR MUST include only the transport address whose address
110a8c39dc0Srenato * family is the same as that of the IP packet carrying the Hello
111a8c39dc0Srenato * message".
112a8c39dc0Srenato */
113a8c39dc0Srenato switch (af) {
114a8c39dc0Srenato case AF_INET:
11565f1d9c1Srenato err |= gen_opt4_hello_prms_tlv(buf, TLV_TYPE_IPV4TRANSADDR,
116a8c39dc0Srenato leconf->ipv4.trans_addr.v4.s_addr);
117a8c39dc0Srenato break;
118a8c39dc0Srenato case AF_INET6:
11965f1d9c1Srenato err |= gen_opt16_hello_prms_tlv(buf, TLV_TYPE_IPV6TRANSADDR,
120a8c39dc0Srenato leconf->ipv6.trans_addr.v6.s6_addr);
121a8c39dc0Srenato break;
122a8c39dc0Srenato default:
123a8c39dc0Srenato fatalx("send_hello: unknown af");
124a8c39dc0Srenato }
125a8c39dc0Srenato
12665f1d9c1Srenato err |= gen_opt4_hello_prms_tlv(buf, TLV_TYPE_CONFIG,
1270e35860dSrenato htonl(global.conf_seqnum));
1280e35860dSrenato
129a8c39dc0Srenato /*
130a8c39dc0Srenato * RFC 7552 - Section 6.1.1:
131a8c39dc0Srenato * "A Dual-stack LSR (i.e., an LSR supporting Dual-stack LDP for a peer)
132a8c39dc0Srenato * MUST include the Dual-Stack capability TLV in all of its LDP Hellos".
133a8c39dc0Srenato */
134a8c39dc0Srenato if (ldp_is_dual_stack(leconf))
13565f1d9c1Srenato err |= gen_ds_hello_prms_tlv(buf, leconf->trans_pref);
13665f1d9c1Srenato
13765f1d9c1Srenato if (err) {
13865f1d9c1Srenato ibuf_free(buf);
13965f1d9c1Srenato return (-1);
14065f1d9c1Srenato }
141a8c39dc0Srenato
142*8bc5e3a3Sclaudio send_packet(fd, af, &dst, ia, ibuf_data(buf), ibuf_size(buf));
143e39620e5Snicm ibuf_free(buf);
144ab0c2486Smichele
145ab0c2486Smichele return (0);
146ab0c2486Smichele }
147ab0c2486Smichele
148ab0c2486Smichele void
recv_hello(struct in_addr lsr_id,struct ldp_msg * msg,int af,union ldpd_addr * src,struct iface * iface,int multicast,char * buf,uint16_t len)14960e1e0e7Srenato recv_hello(struct in_addr lsr_id, struct ldp_msg *msg, int af,
150a8c39dc0Srenato union ldpd_addr *src, struct iface *iface, int multicast, char *buf,
151a8c39dc0Srenato uint16_t len)
152ab0c2486Smichele {
153a8c39dc0Srenato struct adj *adj = NULL;
15420eeeb6fSrenato struct nbr *nbr, *nbrt;
1553de94509Srenato uint16_t holdtime, flags;
156a8c39dc0Srenato int tlvs_rcvd;
157a8c39dc0Srenato int ds_tlv;
158a8c39dc0Srenato union ldpd_addr trans_addr;
159a8c39dc0Srenato uint32_t scope_id = 0;
1600e35860dSrenato uint32_t conf_seqnum;
161a8c39dc0Srenato uint16_t trans_pref;
16289f23408Sclaudio int r;
16383dcf737Sclaudio struct hello_source source;
164a8c39dc0Srenato struct iface_af *ia = NULL;
16583dcf737Sclaudio struct tnbr *tnbr = NULL;
166ab0c2486Smichele
16789f23408Sclaudio r = tlv_decode_hello_prms(buf, len, &holdtime, &flags);
16889f23408Sclaudio if (r == -1) {
169d99a8fc3Srenato log_debug("%s: lsr-id %s: failed to decode params", __func__,
170bde0ea4fSclaudio inet_ntoa(lsr_id));
171ab0c2486Smichele return;
17289f23408Sclaudio }
173caebcba6Srenato /* safety checks */
17469eaa65aSrenato if (holdtime != 0 && holdtime < MIN_HOLDTIME) {
175d99a8fc3Srenato log_debug("%s: lsr-id %s: invalid hello holdtime (%u)",
17669eaa65aSrenato __func__, inet_ntoa(lsr_id), holdtime);
17769eaa65aSrenato return;
17869eaa65aSrenato }
179d7525efdSrenato if (multicast && (flags & F_HELLO_TARGETED)) {
180d99a8fc3Srenato log_debug("%s: lsr-id %s: multicast targeted hello", __func__,
181ac5a809bSrenato inet_ntoa(lsr_id));
182ac5a809bSrenato return;
183ac5a809bSrenato }
184d7525efdSrenato if (!multicast && !((flags & F_HELLO_TARGETED))) {
185d99a8fc3Srenato log_debug("%s: lsr-id %s: unicast link hello", __func__,
186ac5a809bSrenato inet_ntoa(lsr_id));
187ac5a809bSrenato return;
188ac5a809bSrenato }
189caebcba6Srenato buf += r;
190caebcba6Srenato len -= r;
191caebcba6Srenato
192caebcba6Srenato r = tlv_decode_opt_hello_prms(buf, len, &tlvs_rcvd, af, &trans_addr,
1930e35860dSrenato &conf_seqnum, &trans_pref);
194caebcba6Srenato if (r == -1) {
195caebcba6Srenato log_debug("%s: lsr-id %s: failed to decode optional params",
196caebcba6Srenato __func__, inet_ntoa(lsr_id));
197caebcba6Srenato return;
198caebcba6Srenato }
199caebcba6Srenato if (r != len) {
200caebcba6Srenato log_debug("%s: lsr-id %s: unexpected data in message",
201caebcba6Srenato __func__, inet_ntoa(lsr_id));
202caebcba6Srenato return;
203caebcba6Srenato }
204caebcba6Srenato
205caebcba6Srenato /* implicit transport address */
206caebcba6Srenato if (!(tlvs_rcvd & F_HELLO_TLV_RCVD_ADDR))
207caebcba6Srenato trans_addr = *src;
208caebcba6Srenato if (bad_addr(af, &trans_addr)) {
209caebcba6Srenato log_debug("%s: lsr-id %s: invalid transport address %s",
210caebcba6Srenato __func__, inet_ntoa(lsr_id), log_addr(af, &trans_addr));
211caebcba6Srenato return;
212caebcba6Srenato }
213caebcba6Srenato if (af == AF_INET6 && IN6_IS_SCOPE_EMBED(&trans_addr.v6)) {
214caebcba6Srenato /*
215caebcba6Srenato * RFC 7552 - Section 6.1:
21620eeeb6fSrenato * "An LSR MUST use a global unicast IPv6 address in an IPv6
217caebcba6Srenato * Transport Address optional object of outgoing targeted
218caebcba6Srenato * Hellos and check for the same in incoming targeted Hellos
219caebcba6Srenato * (i.e., MUST discard the targeted Hello if it failed the
220caebcba6Srenato * check)".
221caebcba6Srenato */
222d7525efdSrenato if (flags & F_HELLO_TARGETED) {
22320eeeb6fSrenato log_debug("%s: lsr-id %s: invalid targeted hello "
22420eeeb6fSrenato "transport address %s", __func__, inet_ntoa(lsr_id),
22520eeeb6fSrenato log_addr(af, &trans_addr));
226caebcba6Srenato return;
22720eeeb6fSrenato }
228caebcba6Srenato scope_id = iface->ifindex;
229caebcba6Srenato }
230ab0c2486Smichele
2313de94509Srenato memset(&source, 0, sizeof(source));
232ddc8fec1Syasuoka source.lsr_id = lsr_id;
233d7525efdSrenato if (flags & F_HELLO_TARGETED) {
234a8c39dc0Srenato /*
235a8c39dc0Srenato * RFC 7552 - Section 5.2:
236a8c39dc0Srenato * "The link-local IPv6 addresses MUST NOT be used as the
237d3e006a4Srenato * targeted LDP Hello packet's source or destination addresses".
238a8c39dc0Srenato */
239a8c39dc0Srenato if (af == AF_INET6 && IN6_IS_SCOPE_EMBED(&src->v6)) {
240a8c39dc0Srenato log_debug("%s: lsr-id %s: targeted hello with "
241a8c39dc0Srenato "link-local source address", __func__,
242a8c39dc0Srenato inet_ntoa(lsr_id));
243a8c39dc0Srenato return;
244a8c39dc0Srenato }
245a8c39dc0Srenato
246a8c39dc0Srenato tnbr = tnbr_find(leconf, af, src);
24799170248Srenato
24899170248Srenato /* remove the dynamic tnbr if the 'R' bit was cleared */
24999170248Srenato if (tnbr && (tnbr->flags & F_TNBR_DYNAMIC) &&
250d7525efdSrenato !((flags & F_HELLO_REQ_TARG))) {
25199170248Srenato tnbr->flags &= ~F_TNBR_DYNAMIC;
25299170248Srenato tnbr = tnbr_check(tnbr);
25399170248Srenato }
25499170248Srenato
25583dcf737Sclaudio if (!tnbr) {
256d7525efdSrenato if (!((flags & F_HELLO_REQ_TARG) &&
257a8c39dc0Srenato ((ldp_af_conf_get(leconf, af))->flags &
258a8c39dc0Srenato F_LDPD_AF_THELLO_ACCEPT)))
25983dcf737Sclaudio return;
26083dcf737Sclaudio
261a8c39dc0Srenato tnbr = tnbr_new(leconf, af, src);
26299170248Srenato tnbr->flags |= F_TNBR_DYNAMIC;
263b5921293Srenato tnbr_update(tnbr);
26483dcf737Sclaudio LIST_INSERT_HEAD(&leconf->tnbr_list, tnbr, entry);
26583dcf737Sclaudio }
26699170248Srenato
26783dcf737Sclaudio source.type = HELLO_TARGETED;
26883dcf737Sclaudio source.target = tnbr;
26983dcf737Sclaudio } else {
270a8c39dc0Srenato ia = iface_af_get(iface, af);
27183dcf737Sclaudio source.type = HELLO_LINK;
272a8c39dc0Srenato source.link.ia = ia;
273a8c39dc0Srenato source.link.src_addr = *src;
27483dcf737Sclaudio }
27583dcf737Sclaudio
276a8c39dc0Srenato adj = adj_find(&source);
277a8c39dc0Srenato nbr = nbr_find_ldpid(lsr_id.s_addr);
278a8c39dc0Srenato
279a8c39dc0Srenato /* check dual-stack tlv */
280a8c39dc0Srenato ds_tlv = (tlvs_rcvd & F_HELLO_TLV_RCVD_DS) ? 1 : 0;
281a8c39dc0Srenato if (ds_tlv && trans_pref != leconf->trans_pref) {
282a8c39dc0Srenato /*
283a8c39dc0Srenato * RFC 7552 - Section 6.1.1:
284a8c39dc0Srenato * "If the Dual-Stack capability TLV is present and the remote
285a8c39dc0Srenato * preference does not match the local preference (or does not
286a8c39dc0Srenato * get recognized), then the LSR MUST discard the Hello message
287a8c39dc0Srenato * and log an error.
288a8c39dc0Srenato * If an LDP session was already in place, then the LSR MUST
289a8c39dc0Srenato * send a fatal Notification message with status code of
290a8c39dc0Srenato * 'Transport Connection Mismatch' and reset the session".
291a8c39dc0Srenato */
292a8c39dc0Srenato log_debug("%s: lsr-id %s: remote transport preference does not "
293a8c39dc0Srenato "match the local preference", __func__, inet_ntoa(lsr_id));
294a8c39dc0Srenato if (nbr)
29560e1e0e7Srenato session_shutdown(nbr, S_TRANS_MISMTCH, msg->id,
29660e1e0e7Srenato msg->type);
297a8c39dc0Srenato if (adj)
298e373a269Srenato adj_del(adj, S_SHUTDOWN);
299ac5a809bSrenato return;
300ac5a809bSrenato }
301ac5a809bSrenato
302a8c39dc0Srenato /*
303a8c39dc0Srenato * Check for noncompliant dual-stack neighbor according to
304a8c39dc0Srenato * RFC 7552 section 6.1.1.
305a8c39dc0Srenato */
30620eeeb6fSrenato if (nbr && !ds_tlv) {
307a8c39dc0Srenato switch (af) {
308a8c39dc0Srenato case AF_INET:
309a8c39dc0Srenato if (nbr_adj_count(nbr, AF_INET6) > 0) {
310a8c39dc0Srenato session_shutdown(nbr, S_DS_NONCMPLNCE,
31160e1e0e7Srenato msg->id, msg->type);
312a8c39dc0Srenato return;
313a8c39dc0Srenato }
314a8c39dc0Srenato break;
315a8c39dc0Srenato case AF_INET6:
316a8c39dc0Srenato if (nbr_adj_count(nbr, AF_INET) > 0) {
317a8c39dc0Srenato session_shutdown(nbr, S_DS_NONCMPLNCE,
31860e1e0e7Srenato msg->id, msg->type);
319a8c39dc0Srenato return;
320a8c39dc0Srenato }
321a8c39dc0Srenato break;
322a8c39dc0Srenato default:
323a8c39dc0Srenato fatalx("recv_hello: unknown af");
324a8c39dc0Srenato }
325a8c39dc0Srenato }
326a8c39dc0Srenato
327a8c39dc0Srenato /*
32820eeeb6fSrenato * Protections against misconfigured networks and buggy implementations.
329a8c39dc0Srenato */
33020eeeb6fSrenato if (nbr && nbr->af == af &&
331a8c39dc0Srenato (ldp_addrcmp(af, &nbr->raddr, &trans_addr) ||
332a8c39dc0Srenato nbr->raddr_scope != scope_id)) {
33320eeeb6fSrenato log_warnx("%s: lsr-id %s: hello packet advertising a different "
33420eeeb6fSrenato "transport address", __func__, inet_ntoa(lsr_id));
33520eeeb6fSrenato if (adj)
336e373a269Srenato adj_del(adj, S_SHUTDOWN);
33720eeeb6fSrenato return;
33820eeeb6fSrenato }
33920eeeb6fSrenato if (nbr == NULL) {
34020eeeb6fSrenato nbrt = nbr_find_addr(af, &trans_addr);
34120eeeb6fSrenato if (nbrt) {
34220eeeb6fSrenato log_debug("%s: transport address %s is already being "
34320eeeb6fSrenato "used by lsr-id %s", __func__, log_addr(af,
34420eeeb6fSrenato &trans_addr), inet_ntoa(nbrt->id));
34520eeeb6fSrenato if (adj)
346e373a269Srenato adj_del(adj, S_SHUTDOWN);
347a8c39dc0Srenato return;
34883dcf737Sclaudio }
349ab0c2486Smichele }
350ab0c2486Smichele
35120eeeb6fSrenato if (adj == NULL) {
35220eeeb6fSrenato adj = adj_new(lsr_id, &source, &trans_addr);
35320eeeb6fSrenato if (nbr) {
35420eeeb6fSrenato adj->nbr = nbr;
35520eeeb6fSrenato LIST_INSERT_HEAD(&nbr->adj_list, adj, nbr_entry);
35620eeeb6fSrenato }
35720eeeb6fSrenato }
35820eeeb6fSrenato
35920eeeb6fSrenato /*
36020eeeb6fSrenato * If the hello adjacency's address-family doesn't match the local
36120eeeb6fSrenato * preference, then an adjacency is still created but we don't attempt
36220eeeb6fSrenato * to start an LDP session.
36320eeeb6fSrenato */
36420eeeb6fSrenato if (nbr == NULL && (!ds_tlv ||
3658eeefd1dSrenato ((trans_pref == DUAL_STACK_LDPOV4 && af == AF_INET) ||
3668eeefd1dSrenato (trans_pref == DUAL_STACK_LDPOV6 && af == AF_INET6))))
36720eeeb6fSrenato nbr = nbr_new(lsr_id, af, ds_tlv, &trans_addr, scope_id);
36820eeeb6fSrenato
3695ff72af8Srenato /* dynamic LDPv4 GTSM negotiation as per RFC 6720 */
3705ff72af8Srenato if (nbr) {
371d7525efdSrenato if (flags & F_HELLO_GTSM)
3725ff72af8Srenato nbr->flags |= F_NBR_GTSM_NEGOTIATED;
3735ff72af8Srenato else
3745ff72af8Srenato nbr->flags &= ~F_NBR_GTSM_NEGOTIATED;
3755ff72af8Srenato }
3765ff72af8Srenato
3770e35860dSrenato /* update neighbor's configuration sequence number */
3780e35860dSrenato if (nbr && (tlvs_rcvd & F_HELLO_TLV_RCVD_CONF)) {
3790e35860dSrenato if (conf_seqnum > nbr->conf_seqnum &&
3800e35860dSrenato nbr_pending_idtimer(nbr))
3810e35860dSrenato nbr_stop_idtimer(nbr);
3820e35860dSrenato nbr->conf_seqnum = conf_seqnum;
3830e35860dSrenato }
3840e35860dSrenato
38583dcf737Sclaudio /* always update the holdtime to properly handle runtime changes */
38683dcf737Sclaudio switch (source.type) {
38783dcf737Sclaudio case HELLO_LINK:
38883dcf737Sclaudio if (holdtime == 0)
38983dcf737Sclaudio holdtime = LINK_DFLT_HOLDTIME;
39071256830Srenato
391a8c39dc0Srenato adj->holdtime = min(ia->hello_holdtime, holdtime);
39283dcf737Sclaudio break;
39383dcf737Sclaudio case HELLO_TARGETED:
39483dcf737Sclaudio if (holdtime == 0)
39583dcf737Sclaudio holdtime = TARGETED_DFLT_HOLDTIME;
39671256830Srenato
39771256830Srenato adj->holdtime = min(tnbr->hello_holdtime, holdtime);
39883dcf737Sclaudio }
39983dcf737Sclaudio if (adj->holdtime != INFINITE_HOLDTIME)
40083dcf737Sclaudio adj_start_itimer(adj);
40183dcf737Sclaudio else
40283dcf737Sclaudio adj_stop_itimer(adj);
403ab0c2486Smichele
40420eeeb6fSrenato if (nbr && nbr->state == NBR_STA_PRESENT && !nbr_pending_idtimer(nbr) &&
40520eeeb6fSrenato nbr_session_active_role(nbr) && !nbr_pending_connect(nbr))
4061f7e7d77Sclaudio nbr_establish_connection(nbr);
407ab0c2486Smichele }
408ab0c2486Smichele
409c28a25a1Srenato static int
gen_hello_prms_tlv(struct ibuf * buf,uint16_t holdtime,uint16_t flags)4103de94509Srenato gen_hello_prms_tlv(struct ibuf *buf, uint16_t holdtime, uint16_t flags)
411ab0c2486Smichele {
412ab0c2486Smichele struct hello_prms_tlv parms;
413ab0c2486Smichele
4143de94509Srenato memset(&parms, 0, sizeof(parms));
415ab0c2486Smichele parms.type = htons(TLV_TYPE_COMMONHELLO);
416f9479091Sclaudio parms.length = htons(sizeof(parms.holdtime) + sizeof(parms.flags));
41783dcf737Sclaudio parms.holdtime = htons(holdtime);
41883dcf737Sclaudio parms.flags = htons(flags);
419ab0c2486Smichele
420e39620e5Snicm return (ibuf_add(buf, &parms, sizeof(parms)));
421ab0c2486Smichele }
422ab0c2486Smichele
423c28a25a1Srenato static int
gen_opt4_hello_prms_tlv(struct ibuf * buf,uint16_t type,uint32_t value)4243de94509Srenato gen_opt4_hello_prms_tlv(struct ibuf *buf, uint16_t type, uint32_t value)
425f9479091Sclaudio {
426f9479091Sclaudio struct hello_prms_opt4_tlv parms;
427f9479091Sclaudio
4283de94509Srenato memset(&parms, 0, sizeof(parms));
42983dcf737Sclaudio parms.type = htons(type);
430a8c39dc0Srenato parms.length = htons(sizeof(parms.value));
43183dcf737Sclaudio parms.value = value;
432f9479091Sclaudio
433f9479091Sclaudio return (ibuf_add(buf, &parms, sizeof(parms)));
434f9479091Sclaudio }
435f9479091Sclaudio
436c28a25a1Srenato static int
gen_opt16_hello_prms_tlv(struct ibuf * buf,uint16_t type,uint8_t * value)437a8c39dc0Srenato gen_opt16_hello_prms_tlv(struct ibuf *buf, uint16_t type, uint8_t *value)
438a8c39dc0Srenato {
439a8c39dc0Srenato struct hello_prms_opt16_tlv parms;
440a8c39dc0Srenato
441a8c39dc0Srenato memset(&parms, 0, sizeof(parms));
442a8c39dc0Srenato parms.type = htons(type);
443a8c39dc0Srenato parms.length = htons(sizeof(parms.value));
444a8c39dc0Srenato memcpy(&parms.value, value, sizeof(parms.value));
445a8c39dc0Srenato
446a8c39dc0Srenato return (ibuf_add(buf, &parms, sizeof(parms)));
447a8c39dc0Srenato }
448a8c39dc0Srenato
449c28a25a1Srenato static int
gen_ds_hello_prms_tlv(struct ibuf * buf,uint32_t value)450a8c39dc0Srenato gen_ds_hello_prms_tlv(struct ibuf *buf, uint32_t value)
451a8c39dc0Srenato {
452a8c39dc0Srenato if (leconf->flags & F_LDPD_DS_CISCO_INTEROP)
453a8c39dc0Srenato value = htonl(value);
454a8c39dc0Srenato else
455a8c39dc0Srenato value = htonl(value << 28);
456a8c39dc0Srenato
457a8c39dc0Srenato return (gen_opt4_hello_prms_tlv(buf, TLV_TYPE_DUALSTACK, value));
458a8c39dc0Srenato }
459a8c39dc0Srenato
460c28a25a1Srenato static int
tlv_decode_hello_prms(char * buf,uint16_t len,uint16_t * holdtime,uint16_t * flags)4613de94509Srenato tlv_decode_hello_prms(char *buf, uint16_t len, uint16_t *holdtime,
4623de94509Srenato uint16_t *flags)
463ab0c2486Smichele {
46489f23408Sclaudio struct hello_prms_tlv tlv;
465ab0c2486Smichele
46689f23408Sclaudio if (len < sizeof(tlv))
46789f23408Sclaudio return (-1);
4683de94509Srenato memcpy(&tlv, buf, sizeof(tlv));
469ab0c2486Smichele
47089f23408Sclaudio if (tlv.type != htons(TLV_TYPE_COMMONHELLO))
47189f23408Sclaudio return (-1);
47260e1e0e7Srenato if (ntohs(tlv.length) != sizeof(tlv) - TLV_HDR_SIZE)
473ac5a809bSrenato return (-1);
474ab0c2486Smichele
47589f23408Sclaudio *holdtime = ntohs(tlv.holdtime);
47689f23408Sclaudio *flags = ntohs(tlv.flags);
477ab0c2486Smichele
47889f23408Sclaudio return (sizeof(tlv));
479ab0c2486Smichele }
480ab0c2486Smichele
481c28a25a1Srenato static int
tlv_decode_opt_hello_prms(char * buf,uint16_t len,int * tlvs_rcvd,int af,union ldpd_addr * addr,uint32_t * conf_number,uint16_t * trans_pref)482a8c39dc0Srenato tlv_decode_opt_hello_prms(char *buf, uint16_t len, int *tlvs_rcvd, int af,
483a8c39dc0Srenato union ldpd_addr *addr, uint32_t *conf_number, uint16_t *trans_pref)
484ab0c2486Smichele {
4854bde5742Sclaudio struct tlv tlv;
4863de94509Srenato uint16_t tlv_len;
4873de94509Srenato int total = 0;
4880493f73aSmichele
489a8c39dc0Srenato *tlvs_rcvd = 0;
4903de94509Srenato memset(addr, 0, sizeof(*addr));
4910493f73aSmichele *conf_number = 0;
492a8c39dc0Srenato *trans_pref = 0;
4930493f73aSmichele
494a8c39dc0Srenato /*
495a8c39dc0Srenato * RFC 7552 - Section 6.1:
496a8c39dc0Srenato * "An LSR SHOULD accept the Hello message that contains both IPv4 and
497a8c39dc0Srenato * IPv6 Transport Address optional objects but MUST use only the
498a8c39dc0Srenato * transport address whose address family is the same as that of the
499a8c39dc0Srenato * IP packet carrying the Hello message. An LSR SHOULD accept only
500a8c39dc0Srenato * the first Transport Address optional object for a given address
501a8c39dc0Srenato * family in the received Hello message and ignore the rest if the
502a8c39dc0Srenato * LSR receives more than one Transport Address optional object for a
503a8c39dc0Srenato * given address family".
504a8c39dc0Srenato */
50589f23408Sclaudio while (len >= sizeof(tlv)) {
50660e1e0e7Srenato memcpy(&tlv, buf, TLV_HDR_SIZE);
507a78ea73fSrenato tlv_len = ntohs(tlv.length);
508a78ea73fSrenato if (tlv_len + TLV_HDR_SIZE > len)
509a78ea73fSrenato return (-1);
51060e1e0e7Srenato buf += TLV_HDR_SIZE;
51160e1e0e7Srenato len -= TLV_HDR_SIZE;
51260e1e0e7Srenato total += TLV_HDR_SIZE;
5132c943183Srenato
51489f23408Sclaudio switch (ntohs(tlv.type)) {
5150493f73aSmichele case TLV_TYPE_IPV4TRANSADDR:
516a8c39dc0Srenato if (tlv_len != sizeof(addr->v4))
5174bde5742Sclaudio return (-1);
518a87a4ad1Srenato if (af != AF_INET)
519a87a4ad1Srenato return (-1);
520a87a4ad1Srenato if (*tlvs_rcvd & F_HELLO_TLV_RCVD_ADDR)
521a8c39dc0Srenato break;
5222c943183Srenato memcpy(&addr->v4, buf, sizeof(addr->v4));
523a8c39dc0Srenato *tlvs_rcvd |= F_HELLO_TLV_RCVD_ADDR;
524a8c39dc0Srenato break;
525a8c39dc0Srenato case TLV_TYPE_IPV6TRANSADDR:
526a8c39dc0Srenato if (tlv_len != sizeof(addr->v6))
527a8c39dc0Srenato return (-1);
528a87a4ad1Srenato if (af != AF_INET6)
529a87a4ad1Srenato return (-1);
530a87a4ad1Srenato if (*tlvs_rcvd & F_HELLO_TLV_RCVD_ADDR)
531a8c39dc0Srenato break;
5322c943183Srenato memcpy(&addr->v6, buf, sizeof(addr->v6));
533a8c39dc0Srenato *tlvs_rcvd |= F_HELLO_TLV_RCVD_ADDR;
5340493f73aSmichele break;
5350493f73aSmichele case TLV_TYPE_CONFIG:
5363de94509Srenato if (tlv_len != sizeof(uint32_t))
5374bde5742Sclaudio return (-1);
5382c943183Srenato memcpy(conf_number, buf, sizeof(uint32_t));
539a8c39dc0Srenato *tlvs_rcvd |= F_HELLO_TLV_RCVD_CONF;
540a8c39dc0Srenato break;
541a8c39dc0Srenato case TLV_TYPE_DUALSTACK:
542a8c39dc0Srenato if (tlv_len != sizeof(uint32_t))
543a8c39dc0Srenato return (-1);
544a8c39dc0Srenato /*
545a8c39dc0Srenato * RFC 7552 - Section 6.1:
546a8c39dc0Srenato * "A Single-stack LSR does not need to use the
547a8c39dc0Srenato * Dual-Stack capability in Hello messages and SHOULD
548a8c39dc0Srenato * ignore this capability if received".
549a8c39dc0Srenato */
550a8c39dc0Srenato if (!ldp_is_dual_stack(leconf))
551a8c39dc0Srenato break;
552a8c39dc0Srenato /* Shame on you, Cisco! */
553a8c39dc0Srenato if (leconf->flags & F_LDPD_DS_CISCO_INTEROP) {
5542c943183Srenato memcpy(trans_pref, buf + sizeof(uint16_t),
5552c943183Srenato sizeof(uint16_t));
556a8c39dc0Srenato *trans_pref = ntohs(*trans_pref);
557a8c39dc0Srenato } else {
5582c943183Srenato memcpy(trans_pref, buf , sizeof(uint16_t));
559a8c39dc0Srenato *trans_pref = ntohs(*trans_pref) >> 12;
560a8c39dc0Srenato }
561a8c39dc0Srenato *tlvs_rcvd |= F_HELLO_TLV_RCVD_DS;
5620493f73aSmichele break;
5630493f73aSmichele default:
5644bde5742Sclaudio /* if unknown flag set, ignore TLV */
5654bde5742Sclaudio if (!(ntohs(tlv.type) & UNKNOWN_FLAG))
5660493f73aSmichele return (-1);
5674bde5742Sclaudio break;
5680493f73aSmichele }
5692c943183Srenato buf += tlv_len;
5702c943183Srenato len -= tlv_len;
5712c943183Srenato total += tlv_len;
5720493f73aSmichele }
5730493f73aSmichele
5742d825b92Srenato return (total);
575ab0c2486Smichele }
576