1 /* $OpenBSD: neighbor.c,v 1.11 2023/03/08 04:43:13 guenther Exp $ */
2
3 /*
4 * Copyright (c) 2015 Renato Westphal <renato@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
21 #include <string.h>
22 #include <stdlib.h>
23
24 #include "eigrpd.h"
25 #include "eigrpe.h"
26 #include "rde.h"
27 #include "log.h"
28
29 static __inline int nbr_compare(struct nbr *, struct nbr *);
30 static __inline int nbr_pid_compare(struct nbr *, struct nbr *);
31 static void nbr_update_peerid(struct nbr *);
32 static void nbr_timeout(int, short, void *);
33 static void nbr_stop_timeout(struct nbr *);
34
35 RB_GENERATE(nbr_addr_head, nbr, addr_tree, nbr_compare)
36 RB_GENERATE(nbr_pid_head, nbr, pid_tree, nbr_pid_compare)
37
38 struct nbr_pid_head nbrs_by_pid = RB_INITIALIZER(&nbrs_by_pid);
39
40 static __inline int
nbr_compare(struct nbr * a,struct nbr * b)41 nbr_compare(struct nbr *a, struct nbr *b)
42 {
43 if (a->ei->iface->ifindex < b->ei->iface->ifindex)
44 return (-1);
45 if (a->ei->iface->ifindex > b->ei->iface->ifindex)
46 return (1);
47
48 return (eigrp_addrcmp(a->ei->eigrp->af, &a->addr, &b->addr));
49 }
50
51 static __inline int
nbr_pid_compare(struct nbr * a,struct nbr * b)52 nbr_pid_compare(struct nbr *a, struct nbr *b)
53 {
54 return (a->peerid - b->peerid);
55 }
56
57 struct nbr *
nbr_new(struct eigrp_iface * ei,union eigrpd_addr * addr,uint16_t holdtime,int self)58 nbr_new(struct eigrp_iface *ei, union eigrpd_addr *addr, uint16_t holdtime,
59 int self)
60 {
61 struct eigrp *eigrp = ei->eigrp;
62 struct nbr *nbr;
63
64 if (!self)
65 log_debug("%s: interface %s addr %s as %u", __func__,
66 ei->iface->name, log_addr(eigrp->af, addr), eigrp->as);
67
68 if ((nbr = calloc(1, sizeof(*nbr))) == NULL)
69 fatal("nbr_new");
70
71 nbr->ei = ei;
72 TAILQ_INSERT_TAIL(&ei->nbr_list, nbr, entry);
73 nbr->addr = *addr;
74 nbr->peerid = 0;
75 nbr->hello_holdtime = holdtime;
76 nbr->flags = F_EIGRP_NBR_PENDING;
77 if (self)
78 nbr->flags |= F_EIGRP_NBR_SELF;
79 TAILQ_INIT(&nbr->update_list);
80 TAILQ_INIT(&nbr->query_list);
81 TAILQ_INIT(&nbr->reply_list);
82 TAILQ_INIT(&nbr->retrans_list);
83
84 if (RB_INSERT(nbr_addr_head, &eigrp->nbrs, nbr) != NULL)
85 fatalx("nbr_new: RB_INSERT(eigrp->nbrs) failed");
86
87 /* timeout handling */
88 if (!self) {
89 evtimer_set(&nbr->ev_ack, rtp_ack_timer, nbr);
90 evtimer_set(&nbr->ev_hello_timeout, nbr_timeout, nbr);
91 nbr_start_timeout(nbr);
92 }
93
94 return (nbr);
95 }
96
97 void
nbr_init(struct nbr * nbr)98 nbr_init(struct nbr *nbr)
99 {
100 struct timeval now;
101 struct rde_nbr rnbr;
102
103 nbr->flags &= ~F_EIGRP_NBR_PENDING;
104
105 gettimeofday(&now, NULL);
106 nbr->uptime = now.tv_sec;
107
108 nbr_update_peerid(nbr);
109
110 memset(&rnbr, 0, sizeof(rnbr));
111 rnbr.addr = nbr->addr;
112 rnbr.ifaceid = nbr->ei->ifaceid;
113 if (nbr->flags & F_EIGRP_NBR_SELF)
114 rnbr.flags = F_RDE_NBR_SELF|F_RDE_NBR_LOCAL;
115
116 /* rde is not aware of pending nbrs */
117 eigrpe_imsg_compose_rde(IMSG_NEIGHBOR_UP, nbr->peerid, 0, &rnbr,
118 sizeof(rnbr));
119 }
120
121 void
nbr_del(struct nbr * nbr)122 nbr_del(struct nbr *nbr)
123 {
124 struct eigrp *eigrp = nbr->ei->eigrp;
125 struct packet *pkt;
126
127 if (!(nbr->flags & F_EIGRP_NBR_SELF))
128 log_debug("%s: addr %s", __func__,
129 log_addr(eigrp->af, &nbr->addr));
130
131 eigrpe_imsg_compose_rde(IMSG_NEIGHBOR_DOWN, nbr->peerid, 0, NULL, 0);
132
133 nbr_stop_timeout(nbr);
134
135 /* clear retransmission list */
136 while ((pkt = TAILQ_FIRST(&nbr->retrans_list)) != NULL)
137 rtp_packet_del(pkt);
138
139 if (nbr->peerid)
140 RB_REMOVE(nbr_pid_head, &nbrs_by_pid, nbr);
141 RB_REMOVE(nbr_addr_head, &eigrp->nbrs, nbr);
142 TAILQ_REMOVE(&nbr->ei->nbr_list, nbr, entry);
143
144 free(nbr);
145 }
146
147 static void
nbr_update_peerid(struct nbr * nbr)148 nbr_update_peerid(struct nbr *nbr)
149 {
150 static uint32_t peercnt = NBR_CNTSTART;
151
152 if (nbr->peerid)
153 RB_REMOVE(nbr_pid_head, &nbrs_by_pid, nbr);
154
155 /* get next unused peerid */
156 while (nbr_find_peerid(++peercnt))
157 ;
158 nbr->peerid = peercnt;
159
160 if (RB_INSERT(nbr_pid_head, &nbrs_by_pid, nbr) != NULL)
161 fatalx("nbr_new: RB_INSERT(nbrs_by_pid) failed");
162 }
163
164 struct nbr *
nbr_find(struct eigrp_iface * ei,union eigrpd_addr * addr)165 nbr_find(struct eigrp_iface *ei, union eigrpd_addr *addr)
166 {
167 struct nbr n;
168 struct eigrp_iface i;
169 struct eigrp e;
170
171 e.af = ei->eigrp->af;
172 e.as = ei->eigrp->as;
173 i.eigrp = &e;
174 i.iface = ei->iface;
175 n.ei = &i;
176 n.addr = *addr;
177
178 return (RB_FIND(nbr_addr_head, &ei->eigrp->nbrs, &n));
179 }
180
181 struct nbr *
nbr_find_peerid(uint32_t peerid)182 nbr_find_peerid(uint32_t peerid)
183 {
184 struct nbr n;
185 n.peerid = peerid;
186 return (RB_FIND(nbr_pid_head, &nbrs_by_pid, &n));
187 }
188
189 struct ctl_nbr *
nbr_to_ctl(struct nbr * nbr)190 nbr_to_ctl(struct nbr *nbr)
191 {
192 static struct ctl_nbr nctl;
193 struct timeval now;
194
195 nctl.af = nbr->ei->eigrp->af;
196 nctl.as = nbr->ei->eigrp->as;
197 memcpy(nctl.ifname, nbr->ei->iface->name, sizeof(nctl.ifname));
198 nctl.addr = nbr->addr;
199 nctl.hello_holdtime = nbr->hello_holdtime;
200 gettimeofday(&now, NULL);
201 nctl.uptime = now.tv_sec - nbr->uptime;
202
203 return (&nctl);
204 }
205
206 void
nbr_clear_ctl(struct ctl_nbr * nctl)207 nbr_clear_ctl(struct ctl_nbr *nctl)
208 {
209 struct eigrp *eigrp;
210 struct nbr *nbr, *safe;
211
212 TAILQ_FOREACH(eigrp, &econf->instances, entry) {
213 if (nctl->af && nctl->af != eigrp->af)
214 continue;
215 if (nctl->as && nctl->as != eigrp->as)
216 continue;
217
218 RB_FOREACH_SAFE(nbr, nbr_addr_head, &eigrp->nbrs, safe) {
219 if (nbr->flags & (F_EIGRP_NBR_PENDING|F_EIGRP_NBR_SELF))
220 continue;
221 if (eigrp_addrisset(nctl->af, &nctl->addr) &&
222 eigrp_addrcmp(nctl->af, &nctl->addr, &nbr->addr))
223 continue;
224
225 log_debug("%s: neighbor %s manually cleared", __func__,
226 log_addr(nbr->ei->eigrp->af, &nbr->addr));
227 send_peerterm(nbr);
228 nbr_del(nbr);
229 }
230 }
231 }
232
233 /* timers */
234
235 static void
nbr_timeout(int fd,short event,void * arg)236 nbr_timeout(int fd, short event, void *arg)
237 {
238 struct nbr *nbr = arg;
239 struct eigrp *eigrp = nbr->ei->eigrp;
240
241 log_debug("%s: neighbor %s", __func__, log_addr(eigrp->af, &nbr->addr));
242
243 nbr_del(nbr);
244 }
245
246 void
nbr_start_timeout(struct nbr * nbr)247 nbr_start_timeout(struct nbr *nbr)
248 {
249 struct timeval tv;
250
251 timerclear(&tv);
252 tv.tv_sec = nbr->hello_holdtime;
253
254 if (evtimer_add(&nbr->ev_hello_timeout, &tv) == -1)
255 fatal("nbr_start_timeout");
256 }
257
258 static void
nbr_stop_timeout(struct nbr * nbr)259 nbr_stop_timeout(struct nbr *nbr)
260 {
261 if (evtimer_pending(&nbr->ev_hello_timeout, NULL) &&
262 evtimer_del(&nbr->ev_hello_timeout) == -1)
263 fatal("nbr_stop_timeout");
264 }
265