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