1 |
|
|
/* $OpenBSD: neighbor.c,v 1.10 2016/09/02 16:44:33 renato 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 |
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 |
52 |
|
|
nbr_pid_compare(struct nbr *a, struct nbr *b) |
53 |
|
|
{ |
54 |
|
|
return (a->peerid - b->peerid); |
55 |
|
|
} |
56 |
|
|
|
57 |
|
|
struct nbr * |
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 |
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 |
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 |
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 * |
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 * |
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 * |
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 |
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 |
|
|
/* ARGSUSED */ |
236 |
|
|
static void |
237 |
|
|
nbr_timeout(int fd, short event, void *arg) |
238 |
|
|
{ |
239 |
|
|
struct nbr *nbr = arg; |
240 |
|
|
struct eigrp *eigrp = nbr->ei->eigrp; |
241 |
|
|
|
242 |
|
|
log_debug("%s: neighbor %s", __func__, log_addr(eigrp->af, &nbr->addr)); |
243 |
|
|
|
244 |
|
|
nbr_del(nbr); |
245 |
|
|
} |
246 |
|
|
|
247 |
|
|
void |
248 |
|
|
nbr_start_timeout(struct nbr *nbr) |
249 |
|
|
{ |
250 |
|
|
struct timeval tv; |
251 |
|
|
|
252 |
|
|
timerclear(&tv); |
253 |
|
|
tv.tv_sec = nbr->hello_holdtime; |
254 |
|
|
|
255 |
|
|
if (evtimer_add(&nbr->ev_hello_timeout, &tv) == -1) |
256 |
|
|
fatal("nbr_start_timeout"); |
257 |
|
|
} |
258 |
|
|
|
259 |
|
|
static void |
260 |
|
|
nbr_stop_timeout(struct nbr *nbr) |
261 |
|
|
{ |
262 |
|
|
if (evtimer_pending(&nbr->ev_hello_timeout, NULL) && |
263 |
|
|
evtimer_del(&nbr->ev_hello_timeout) == -1) |
264 |
|
|
fatal("nbr_stop_timeout"); |
265 |
|
|
} |