]>
Commit | Line | Data |
---|---|---|
7f57883e DS |
1 | /* |
2 | * EIGRP Network Related Functions. | |
3 | * Copyright (C) 2013-2014 | |
4 | * Authors: | |
5 | * Donnie Savage | |
6 | * Jan Janovic | |
7 | * Matej Perina | |
8 | * Peter Orsag | |
9 | * Peter Paluch | |
10 | * | |
11 | * This file is part of GNU Zebra. | |
12 | * | |
13 | * GNU Zebra is free software; you can redistribute it and/or modify it | |
14 | * under the terms of the GNU General Public License as published by the | |
15 | * Free Software Foundation; either version 2, or (at your option) any | |
16 | * later version. | |
17 | * | |
18 | * GNU Zebra is distributed in the hope that it will be useful, but | |
19 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
20 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
21 | * General Public License for more details. | |
22 | * | |
23 | * You should have received a copy of the GNU General Public License | |
24 | * along with GNU Zebra; see the file COPYING. If not, write to the Free | |
25 | * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA | |
26 | * 02111-1307, USA. | |
27 | */ | |
28 | ||
29 | #include <zebra.h> | |
30 | ||
31 | #include "thread.h" | |
32 | #include "linklist.h" | |
33 | #include "prefix.h" | |
34 | #include "if.h" | |
35 | #include "sockunion.h" | |
36 | #include "log.h" | |
37 | #include "sockopt.h" | |
38 | #include "privs.h" | |
39 | #include "table.h" | |
40 | #include "vty.h" | |
41 | ||
42 | extern struct zebra_privs_t eigrpd_privs; | |
43 | ||
44 | #include "eigrpd/eigrp_structs.h" | |
45 | #include "eigrpd/eigrpd.h" | |
46 | #include "eigrpd/eigrp_interface.h" | |
47 | #include "eigrpd/eigrp_neighbor.h" | |
48 | #include "eigrpd/eigrp_packet.h" | |
49 | #include "eigrpd/eigrp_zebra.h" | |
50 | #include "eigrpd/eigrp_vty.h" | |
51 | #include "eigrpd/eigrp_network.h" | |
52 | ||
53 | static int | |
54 | eigrp_network_match_iface(const struct connected *, const struct prefix *); | |
55 | static void | |
56 | eigrp_network_run_interface(struct eigrp *, struct prefix *, struct interface *); | |
57 | ||
58 | int | |
59 | eigrp_sock_init(void) | |
60 | { | |
61 | int eigrp_sock; | |
62 | int ret, hincl = 1; | |
63 | ||
64 | if (eigrpd_privs.change(ZPRIVS_RAISE)) | |
65 | zlog_err("eigrp_sock_init: could not raise privs, %s", | |
66 | safe_strerror(errno)); | |
67 | ||
68 | eigrp_sock = socket(AF_INET, SOCK_RAW, IPPROTO_EIGRPIGP); | |
69 | if (eigrp_sock < 0) | |
70 | { | |
71 | int save_errno = errno; | |
72 | if (eigrpd_privs.change(ZPRIVS_LOWER)) | |
73 | zlog_err("eigrp_sock_init: could not lower privs, %s", | |
74 | safe_strerror(errno)); | |
75 | zlog_err("eigrp_read_sock_init: socket: %s", safe_strerror(save_errno)); | |
76 | exit(1); | |
77 | } | |
78 | ||
79 | #ifdef IP_HDRINCL | |
80 | /* we will include IP header with packet */ | |
81 | ret = setsockopt(eigrp_sock, IPPROTO_IP, IP_HDRINCL, &hincl, sizeof(hincl)); | |
82 | if (ret < 0) | |
83 | { | |
84 | int save_errno = errno; | |
85 | if (eigrpd_privs.change(ZPRIVS_LOWER)) | |
86 | zlog_err("eigrp_sock_init: could not lower privs, %s", | |
87 | safe_strerror(errno)); | |
88 | zlog_warn("Can't set IP_HDRINCL option for fd %d: %s", eigrp_sock, | |
89 | safe_strerror(save_errno)); | |
90 | ||
91 | } | |
92 | #elif defined (IPTOS_PREC_INTERNETCONTROL) | |
93 | #warning "IP_HDRINCL not available on this system" | |
94 | #warning "using IPTOS_PREC_INTERNETCONTROL" | |
95 | ret = setsockopt_ipv4_tos (eigrp_sock, IPTOS_PREC_INTERNETCONTROL); | |
96 | if (ret < 0) | |
97 | { | |
98 | int save_errno = errno; | |
99 | if ( eigrpd_privs.change (ZPRIVS_LOWER) ) | |
100 | zlog_err ("eigrpd_sock_init: could not lower privs, %s", | |
101 | safe_strerror (errno) ); | |
102 | zlog_warn ("can't set sockopt IP_TOS %d to socket %d: %s", | |
103 | tos, eigrp_sock, safe_strerror (save_errno)); | |
104 | close (eigrp_sock); /* Prevent sd leak. */ | |
105 | return ret; | |
106 | } | |
107 | #else /* !IPTOS_PREC_INTERNETCONTROL */ | |
108 | #warning "IP_HDRINCL not available, nor is IPTOS_PREC_INTERNETCONTROL" | |
109 | zlog_warn ("IP_HDRINCL option not available"); | |
110 | #endif /* IP_HDRINCL */ | |
111 | ||
112 | ret = setsockopt_ifindex(AF_INET, eigrp_sock, 1); | |
113 | ||
114 | if (ret < 0) | |
115 | zlog_warn("Can't set pktinfo option for fd %d", eigrp_sock); | |
116 | ||
117 | if (eigrpd_privs.change(ZPRIVS_LOWER)) | |
118 | { | |
119 | zlog_err("eigrp_sock_init: could not lower privs, %s", | |
120 | safe_strerror(errno)); | |
121 | } | |
122 | ||
123 | return eigrp_sock; | |
124 | } | |
125 | ||
126 | void | |
127 | eigrp_adjust_sndbuflen(struct eigrp * eigrp, unsigned int buflen) | |
128 | { | |
129 | int newbuflen; | |
130 | /* Check if any work has to be done at all. */ | |
131 | if (eigrp->maxsndbuflen >= buflen) | |
132 | return; | |
133 | if (eigrpd_privs.change(ZPRIVS_RAISE)) | |
134 | zlog_err("%s: could not raise privs, %s", __func__, safe_strerror(errno)); | |
135 | /* Now we try to set SO_SNDBUF to what our caller has requested | |
136 | * (the MTU of a newly added interface). However, if the OS has | |
137 | * truncated the actual buffer size to somewhat less size, try | |
138 | * to detect it and update our records appropriately. The OS | |
139 | * may allocate more buffer space, than requested, this isn't | |
140 | * a error. | |
141 | */ | |
142 | setsockopt_so_sendbuf(eigrp->fd, buflen); | |
143 | newbuflen = getsockopt_so_sendbuf(eigrp->fd); | |
144 | if (newbuflen < 0 || newbuflen < (int) buflen) | |
145 | zlog_warn("%s: tried to set SO_SNDBUF to %u, but got %d", __func__, buflen, | |
146 | newbuflen); | |
147 | if (newbuflen >= 0) | |
148 | eigrp->maxsndbuflen = (unsigned int) newbuflen; | |
149 | else | |
150 | zlog_warn("%s: failed to get SO_SNDBUF", __func__); | |
151 | if (eigrpd_privs.change(ZPRIVS_LOWER)) | |
152 | zlog_err("%s: could not lower privs, %s", __func__, safe_strerror(errno)); | |
153 | } | |
154 | ||
155 | int | |
156 | eigrp_if_ipmulticast(struct eigrp *top, struct prefix *p, unsigned int ifindex) | |
157 | { | |
158 | u_char val; | |
159 | int ret, len; | |
160 | ||
161 | val = 0; | |
162 | len = sizeof(val); | |
163 | ||
164 | /* Prevent receiving self-origined multicast packets. */ | |
165 | ret = setsockopt(top->fd, IPPROTO_IP, IP_MULTICAST_LOOP, (void *) &val, len); | |
166 | if (ret < 0) | |
167 | zlog_warn("can't setsockopt IP_MULTICAST_LOOP (0) for fd %d: %s", top->fd, | |
168 | safe_strerror(errno)); | |
169 | ||
170 | /* Explicitly set multicast ttl to 1 -- endo. */ | |
171 | val = 1; | |
172 | ret = setsockopt(top->fd, IPPROTO_IP, IP_MULTICAST_TTL, (void *) &val, len); | |
173 | if (ret < 0) | |
174 | zlog_warn("can't setsockopt IP_MULTICAST_TTL (1) for fd %d: %s", top->fd, | |
175 | safe_strerror(errno)); | |
176 | ||
177 | ret = setsockopt_ipv4_multicast_if(top->fd, p->u.prefix4, ifindex); | |
178 | if (ret < 0) | |
179 | zlog_warn("can't setsockopt IP_MULTICAST_IF (fd %d, addr %s, " | |
180 | "ifindex %u): %s", top->fd, inet_ntoa(p->u.prefix4), ifindex, | |
181 | safe_strerror(errno)); | |
182 | ||
183 | return ret; | |
184 | } | |
185 | ||
186 | /* Join to the EIGRP multicast group. */ | |
187 | int | |
188 | eigrp_if_add_allspfrouters(struct eigrp *top, struct prefix *p, | |
189 | unsigned int ifindex) | |
190 | { | |
191 | int ret; | |
192 | ||
193 | ret = setsockopt_ipv4_multicast(top->fd, IP_ADD_MEMBERSHIP, p->u.prefix4, | |
194 | htonl(EIGRP_MULTICAST_ADDRESS), ifindex); | |
195 | if (ret < 0) | |
196 | zlog_warn("can't setsockopt IP_ADD_MEMBERSHIP (fd %d, addr %s, " | |
197 | "ifindex %u, AllSPFRouters): %s; perhaps a kernel limit " | |
198 | "on # of multicast group memberships has been exceeded?", top->fd, | |
199 | inet_ntoa(p->u.prefix4), ifindex, safe_strerror(errno)); | |
200 | else | |
201 | zlog_debug("interface %s [%u] join EIGRP Multicast group.", | |
202 | inet_ntoa(p->u.prefix4), ifindex); | |
203 | ||
204 | return ret; | |
205 | } | |
206 | ||
207 | int | |
208 | eigrp_if_drop_allspfrouters(struct eigrp *top, struct prefix *p, | |
209 | unsigned int ifindex) | |
210 | { | |
211 | int ret; | |
212 | ||
213 | ret = setsockopt_ipv4_multicast(top->fd, IP_DROP_MEMBERSHIP, p->u.prefix4, | |
214 | htonl(EIGRP_MULTICAST_ADDRESS), ifindex); | |
215 | if (ret < 0) | |
216 | zlog_warn("can't setsockopt IP_DROP_MEMBERSHIP (fd %d, addr %s, " | |
217 | "ifindex %u, AllSPFRouters): %s", top->fd, inet_ntoa(p->u.prefix4), | |
218 | ifindex, safe_strerror(errno)); | |
219 | else | |
220 | zlog_debug("interface %s [%u] leave EIGRP Multicast group.", | |
221 | inet_ntoa(p->u.prefix4), ifindex); | |
222 | ||
223 | return ret; | |
224 | } | |
225 | ||
226 | int | |
227 | eigrp_network_set(struct eigrp *eigrp, struct prefix_ipv4 *p) | |
228 | { | |
229 | struct route_node *rn; | |
230 | struct interface *ifp; | |
231 | struct listnode *node; | |
232 | ||
233 | zlog_debug ("A"); | |
234 | rn = route_node_get(eigrp->networks, (struct prefix *) p); | |
235 | if (rn->info) | |
236 | { | |
237 | /* There is already same network statement. */ | |
238 | route_unlock_node(rn); | |
239 | return 0; | |
240 | } | |
241 | ||
242 | struct prefix_ipv4 *pref = prefix_ipv4_new(); | |
243 | PREFIX_COPY_IPV4(pref,p); | |
244 | rn->info = (void *) pref; | |
245 | ||
246 | zlog_debug ("B"); | |
247 | /* Schedule Router ID Update. */ | |
248 | // if (eigrp->router_id == 0) | |
249 | // eigrp_router_id_update(eigrp); | |
250 | /* Run network config now. */ | |
251 | /* Get target interface. */ | |
252 | for (ALL_LIST_ELEMENTS_RO(vrf_iflist(VRF_DEFAULT), node, ifp)) | |
253 | { | |
254 | zlog_debug("Setting up %s", ifp->name); | |
255 | eigrp_network_run_interface(eigrp, (struct prefix *) p, ifp); | |
256 | } | |
257 | return 1; | |
258 | } | |
259 | ||
260 | /* Check whether interface matches given network | |
261 | * returns: 1, true. 0, false | |
262 | */ | |
263 | static int | |
264 | eigrp_network_match_iface(const struct connected *co, const struct prefix *net) | |
265 | { | |
266 | /* new approach: more elegant and conceptually clean */ | |
267 | return prefix_match(net, CONNECTED_PREFIX (co)); | |
268 | } | |
269 | ||
270 | static void | |
271 | eigrp_network_run_interface(struct eigrp *eigrp, struct prefix *p, | |
272 | struct interface *ifp) | |
273 | { | |
274 | struct listnode *cnode; | |
275 | struct connected *co; | |
276 | ||
277 | /* if interface prefix is match specified prefix, | |
278 | then create socket and join multicast group. */ | |
279 | for (ALL_LIST_ELEMENTS_RO(ifp->connected, cnode, co)) | |
280 | { | |
281 | ||
282 | if (CHECK_FLAG (co->flags,ZEBRA_IFA_SECONDARY)) | |
283 | continue; | |
284 | ||
285 | if (p->family == co->address->family | |
286 | && !eigrp_if_table_lookup(ifp, co->address) | |
287 | && eigrp_network_match_iface(co, p)) | |
288 | { | |
289 | struct eigrp_interface *ei; | |
290 | ||
291 | ei = eigrp_if_new(eigrp, ifp, co->address); | |
292 | ei->connected = co; | |
293 | ||
294 | ei->params = eigrp_lookup_if_params(ifp, ei->address->u.prefix4); | |
295 | ||
296 | /* Relate eigrp interface to eigrp instance. */ | |
297 | ei->eigrp = eigrp; | |
298 | ||
299 | /* update network type as interface flag */ | |
300 | /* If network type is specified previously, | |
301 | skip network type setting. */ | |
302 | ei->type = IF_DEF_PARAMS (ifp)->type; | |
303 | ||
304 | /* if router_id is not configured, dont bring up | |
305 | * interfaces. | |
306 | * eigrp_router_id_update() will call eigrp_if_update | |
307 | * whenever r-id is configured instead. | |
308 | */ | |
309 | if (if_is_operative(ifp)) | |
310 | eigrp_if_up(ei); | |
311 | } | |
312 | } | |
313 | } | |
314 | ||
315 | void | |
316 | eigrp_if_update(struct interface *ifp) | |
317 | { | |
318 | struct listnode *node, *nnode; | |
319 | struct route_node *rn; | |
320 | struct eigrp *eigrp; | |
321 | ||
322 | /* | |
323 | * In the event there are multiple eigrp autonymnous systems running, | |
324 | * we need to check eac one and add the interface as approperate | |
325 | */ | |
326 | for (ALL_LIST_ELEMENTS(eigrp_om->eigrp, node, nnode, eigrp)) | |
327 | { | |
328 | /* EIGRP must be on and Router-ID must be configured. */ | |
329 | if (!eigrp || eigrp->router_id == 0) | |
330 | continue; | |
331 | ||
332 | /* Run each network for this interface. */ | |
333 | for (rn = route_top(eigrp->networks); rn; rn = route_next(rn)) | |
334 | if (rn->info != NULL) | |
335 | { | |
336 | eigrp_network_run_interface(eigrp, &rn->p, ifp); | |
337 | } | |
338 | } | |
339 | } | |
340 | ||
341 | int | |
342 | eigrp_network_unset(struct eigrp *eigrp, struct prefix_ipv4 *p) | |
343 | { | |
344 | struct route_node *rn; | |
345 | struct listnode *node, *nnode; | |
346 | struct eigrp_interface *ei; | |
347 | struct prefix *pref; | |
348 | ||
349 | rn = route_node_lookup(eigrp->networks, (struct prefix *) p); | |
350 | if (rn == NULL) | |
351 | return 0; | |
352 | ||
353 | pref = rn->info; | |
354 | route_unlock_node (rn); | |
355 | ||
356 | if (!IPV4_ADDR_SAME (&pref->u.prefix4, &p->prefix)) | |
357 | return 0; | |
358 | ||
359 | prefix_ipv4_free(rn->info); | |
360 | rn->info = NULL; | |
361 | route_unlock_node(rn); /* initial reference */ | |
362 | ||
363 | /* Find interfaces that not configured already. */ | |
364 | for (ALL_LIST_ELEMENTS(eigrp->eiflist, node, nnode, ei)) | |
365 | { | |
366 | int found = 0; | |
367 | struct connected *co = ei->connected; | |
368 | ||
369 | for (rn = route_top(eigrp->networks); rn; rn = route_next(rn)) | |
370 | { | |
371 | if (rn->info == NULL) | |
372 | continue; | |
373 | ||
374 | if (eigrp_network_match_iface(co, &rn->p)) | |
375 | { | |
376 | zlog_debug("eigrp_network_unset()2"); | |
377 | found = 1; | |
378 | route_unlock_node(rn); | |
379 | break; | |
380 | } | |
381 | } | |
382 | ||
383 | if (found == 0) | |
384 | { | |
385 | eigrp_if_free(ei, INTERFACE_DOWN_BY_VTY); | |
386 | } | |
387 | } | |
388 | ||
389 | return 1; | |
390 | } | |
391 | ||
392 | u_int32_t | |
393 | eigrp_calculate_metrics(struct eigrp *eigrp, struct eigrp_metrics *metric) | |
394 | { | |
395 | u_int64_t temp_metric; | |
396 | temp_metric = 0; | |
397 | ||
398 | if(metric->delay == EIGRP_MAX_METRIC) | |
399 | return EIGRP_MAX_METRIC; | |
400 | ||
401 | // EIGRP Metric = {K1*BW+[(K2*BW)/(256-load)]+(K3*delay)}*{K5/(reliability+K4)} | |
402 | ||
403 | if (eigrp->k_values[0]) | |
404 | temp_metric += (eigrp->k_values[0] * metric->bandwith); | |
405 | if (eigrp->k_values[1]) | |
406 | temp_metric += ((eigrp->k_values[1] * metric->bandwith) | |
407 | / (256 - metric->load)); | |
408 | if (eigrp->k_values[2]) | |
409 | temp_metric += (eigrp->k_values[2] * metric->delay); | |
410 | if (eigrp->k_values[3] && !eigrp->k_values[4]) | |
411 | temp_metric *= eigrp->k_values[3]; | |
412 | if (!eigrp->k_values[3] && eigrp->k_values[4]) | |
413 | temp_metric *= (eigrp->k_values[4] / metric->reliability); | |
414 | if (eigrp->k_values[3] && eigrp->k_values[4]) | |
415 | temp_metric *= ((eigrp->k_values[4] / metric->reliability) | |
416 | + eigrp->k_values[3]); | |
417 | ||
418 | if (temp_metric <= EIGRP_MAX_METRIC) | |
419 | return (u_int32_t) temp_metric; | |
420 | else | |
421 | return EIGRP_MAX_METRIC; | |
422 | } | |
423 | ||
424 | u_int32_t | |
425 | eigrp_calculate_total_metrics(struct eigrp *eigrp, | |
426 | struct eigrp_neighbor_entry *entry) | |
427 | { | |
428 | entry->total_metric = entry->reported_metric; | |
429 | u_int64_t temp_delay = (u_int64_t) entry->total_metric.delay | |
430 | + (u_int64_t) EIGRP_IF_PARAM (entry->ei, delay); | |
431 | entry->total_metric.delay = | |
432 | temp_delay > EIGRP_MAX_METRIC ? EIGRP_MAX_METRIC : (u_int32_t) temp_delay; | |
433 | ||
434 | u_int32_t bw = EIGRP_IF_PARAM (entry->ei,bandwidth); | |
435 | entry->total_metric.bandwith = | |
436 | entry->total_metric.bandwith > bw ? bw : entry->total_metric.bandwith; | |
437 | ||
438 | return eigrp_calculate_metrics(eigrp, &entry->total_metric); | |
439 | } | |
440 | ||
441 | u_char | |
442 | eigrp_metrics_is_same(struct eigrp_metrics *metric1, | |
443 | struct eigrp_metrics *metric2) | |
444 | { | |
445 | if ((metric1->bandwith == metric2->bandwith) | |
446 | && (metric1->delay == metric2->delay) | |
447 | && (metric1->hop_count == metric2->hop_count) | |
448 | && (metric1->load == metric2->load) | |
449 | && (metric1->reliability == metric2->reliability) | |
450 | && (metric1->mtu[0] == metric2->mtu[0]) | |
451 | && (metric1->mtu[1] == metric2->mtu[1]) | |
452 | && (metric1->mtu[2] == metric2->mtu[2])) | |
453 | return 1; | |
454 | ||
455 | return 0; // if different | |
456 | } | |
457 | void | |
458 | eigrp_external_routes_refresh (struct eigrp *eigrp, int type) | |
459 | { | |
460 | ||
461 | ||
462 | } | |
463 |