]>
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" | |
6ae7ed45 | 40 | #include "lib_errors.h" |
7f57883e | 41 | |
7f57883e DS |
42 | #include "eigrpd/eigrp_structs.h" |
43 | #include "eigrpd/eigrpd.h" | |
44 | #include "eigrpd/eigrp_interface.h" | |
45 | #include "eigrpd/eigrp_neighbor.h" | |
46 | #include "eigrpd/eigrp_packet.h" | |
47 | #include "eigrpd/eigrp_zebra.h" | |
48 | #include "eigrpd/eigrp_vty.h" | |
49 | #include "eigrpd/eigrp_network.h" | |
50 | ||
952248db DS |
51 | static int eigrp_network_match_iface(const struct prefix *connected_prefix, |
52 | const struct prefix *prefix); | |
d62a17ae | 53 | static void eigrp_network_run_interface(struct eigrp *, struct prefix *, |
54 | struct interface *); | |
7f57883e | 55 | |
128ed760 | 56 | int eigrp_sock_init(struct vrf *vrf) |
7f57883e | 57 | { |
38513e88 | 58 | int eigrp_sock = -1; |
029a775e | 59 | int ret; |
60 | #ifdef IP_HDRINCL | |
61 | int hincl = 1; | |
62 | #endif | |
d62a17ae | 63 | |
38513e88 DS |
64 | if (!vrf) |
65 | return eigrp_sock; | |
66 | ||
0cf6db21 | 67 | frr_with_privs(&eigrpd_privs) { |
128ed760 DS |
68 | eigrp_sock = vrf_socket( |
69 | AF_INET, SOCK_RAW, IPPROTO_EIGRPIGP, vrf->vrf_id, | |
70 | vrf->vrf_id != VRF_DEFAULT ? vrf->name : NULL); | |
6bb30c2c DL |
71 | if (eigrp_sock < 0) { |
72 | zlog_err("eigrp_read_sock_init: socket: %s", | |
73 | safe_strerror(errno)); | |
74 | exit(1); | |
75 | } | |
7f57883e DS |
76 | |
77 | #ifdef IP_HDRINCL | |
6bb30c2c DL |
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 | zlog_warn("Can't set IP_HDRINCL option for fd %d: %s", | |
83 | eigrp_sock, safe_strerror(errno)); | |
84 | } | |
d62a17ae | 85 | #elif defined(IPTOS_PREC_INTERNETCONTROL) |
7f57883e DS |
86 | #warning "IP_HDRINCL not available on this system" |
87 | #warning "using IPTOS_PREC_INTERNETCONTROL" | |
6bb30c2c DL |
88 | ret = setsockopt_ipv4_tos(eigrp_sock, |
89 | IPTOS_PREC_INTERNETCONTROL); | |
90 | if (ret < 0) { | |
91 | zlog_warn("can't set sockopt IP_TOS %d to socket %d: %s", | |
92 | tos, eigrp_sock, safe_strerror(errno)); | |
93 | close(eigrp_sock); /* Prevent sd leak. */ | |
94 | return ret; | |
95 | } | |
7f57883e DS |
96 | #else /* !IPTOS_PREC_INTERNETCONTROL */ |
97 | #warning "IP_HDRINCL not available, nor is IPTOS_PREC_INTERNETCONTROL" | |
6bb30c2c | 98 | zlog_warn("IP_HDRINCL option not available"); |
7f57883e DS |
99 | #endif /* IP_HDRINCL */ |
100 | ||
6bb30c2c DL |
101 | ret = setsockopt_ifindex(AF_INET, eigrp_sock, 1); |
102 | if (ret < 0) | |
103 | zlog_warn("Can't set pktinfo option for fd %d", | |
104 | eigrp_sock); | |
d62a17ae | 105 | } |
7f57883e | 106 | |
d62a17ae | 107 | return eigrp_sock; |
7f57883e DS |
108 | } |
109 | ||
d62a17ae | 110 | void eigrp_adjust_sndbuflen(struct eigrp *eigrp, unsigned int buflen) |
7f57883e | 111 | { |
d62a17ae | 112 | int newbuflen; |
113 | /* Check if any work has to be done at all. */ | |
114 | if (eigrp->maxsndbuflen >= buflen) | |
115 | return; | |
d62a17ae | 116 | |
117 | /* Now we try to set SO_SNDBUF to what our caller has requested | |
118 | * (the MTU of a newly added interface). However, if the OS has | |
119 | * truncated the actual buffer size to somewhat less size, try | |
120 | * to detect it and update our records appropriately. The OS | |
121 | * may allocate more buffer space, than requested, this isn't | |
122 | * a error. | |
123 | */ | |
338b8e91 RW |
124 | setsockopt_so_sendbuf(eigrp->fd, buflen); |
125 | newbuflen = getsockopt_so_sendbuf(eigrp->fd); | |
126 | if (newbuflen < 0 || newbuflen < (int)buflen) | |
127 | zlog_warn("%s: tried to set SO_SNDBUF to %u, but got %d", | |
128 | __func__, buflen, newbuflen); | |
129 | if (newbuflen >= 0) | |
130 | eigrp->maxsndbuflen = (unsigned int)newbuflen; | |
131 | else | |
132 | zlog_warn("%s: failed to get SO_SNDBUF", __func__); | |
7f57883e DS |
133 | } |
134 | ||
d62a17ae | 135 | int eigrp_if_ipmulticast(struct eigrp *top, struct prefix *p, |
136 | unsigned int ifindex) | |
7f57883e | 137 | { |
d7c0a89a | 138 | uint8_t val; |
d62a17ae | 139 | int ret, len; |
140 | ||
141 | val = 0; | |
142 | len = sizeof(val); | |
143 | ||
144 | /* Prevent receiving self-origined multicast packets. */ | |
145 | ret = setsockopt(top->fd, IPPROTO_IP, IP_MULTICAST_LOOP, (void *)&val, | |
146 | len); | |
147 | if (ret < 0) | |
148 | zlog_warn( | |
149 | "can't setsockopt IP_MULTICAST_LOOP (0) for fd %d: %s", | |
150 | top->fd, safe_strerror(errno)); | |
151 | ||
152 | /* Explicitly set multicast ttl to 1 -- endo. */ | |
153 | val = 1; | |
154 | ret = setsockopt(top->fd, IPPROTO_IP, IP_MULTICAST_TTL, (void *)&val, | |
155 | len); | |
156 | if (ret < 0) | |
157 | zlog_warn("can't setsockopt IP_MULTICAST_TTL (1) for fd %d: %s", | |
158 | top->fd, safe_strerror(errno)); | |
159 | ||
160 | ret = setsockopt_ipv4_multicast_if(top->fd, p->u.prefix4, ifindex); | |
161 | if (ret < 0) | |
162 | zlog_warn( | |
37b4b3cc DS |
163 | "can't setsockopt IP_MULTICAST_IF (fd %d, addr %pI4, ifindex %u): %s", |
164 | top->fd, &p->u.prefix4, ifindex, safe_strerror(errno)); | |
d62a17ae | 165 | |
166 | return ret; | |
7f57883e DS |
167 | } |
168 | ||
169 | /* Join to the EIGRP multicast group. */ | |
d62a17ae | 170 | int eigrp_if_add_allspfrouters(struct eigrp *top, struct prefix *p, |
171 | unsigned int ifindex) | |
7f57883e | 172 | { |
d62a17ae | 173 | int ret; |
174 | ||
175 | ret = setsockopt_ipv4_multicast( | |
176 | top->fd, IP_ADD_MEMBERSHIP, p->u.prefix4, | |
177 | htonl(EIGRP_MULTICAST_ADDRESS), ifindex); | |
178 | if (ret < 0) | |
179 | zlog_warn( | |
37b4b3cc DS |
180 | "can't setsockopt IP_ADD_MEMBERSHIP (fd %d, addr %pI4, ifindex %u, AllSPFRouters): %s; perhaps a kernel limit on # of multicast group memberships has been exceeded?", |
181 | top->fd, &p->u.prefix4, ifindex, safe_strerror(errno)); | |
d62a17ae | 182 | else |
37b4b3cc DS |
183 | zlog_debug("interface %pI4 [%u] join EIGRP Multicast group.", |
184 | &p->u.prefix4, ifindex); | |
d62a17ae | 185 | |
186 | return ret; | |
7f57883e DS |
187 | } |
188 | ||
d62a17ae | 189 | int eigrp_if_drop_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_DROP_MEMBERSHIP, p->u.prefix4, | |
196 | htonl(EIGRP_MULTICAST_ADDRESS), ifindex); | |
197 | if (ret < 0) | |
198 | zlog_warn( | |
37b4b3cc DS |
199 | "can't setsockopt IP_DROP_MEMBERSHIP (fd %d, addr %pI4, ifindex %u, AllSPFRouters): %s", |
200 | top->fd, &p->u.prefix4, ifindex, safe_strerror(errno)); | |
d62a17ae | 201 | else |
37b4b3cc DS |
202 | zlog_debug("interface %pI4 [%u] leave EIGRP Multicast group.", |
203 | &p->u.prefix4, ifindex); | |
d62a17ae | 204 | |
205 | return ret; | |
7f57883e DS |
206 | } |
207 | ||
cd6c066e | 208 | int eigrp_network_set(struct eigrp *eigrp, struct prefix *p) |
7f57883e | 209 | { |
d0ac2662 | 210 | struct vrf *vrf = vrf_lookup_by_id(eigrp->vrf_id); |
d62a17ae | 211 | struct route_node *rn; |
212 | struct interface *ifp; | |
d62a17ae | 213 | |
c4efd0f4 | 214 | rn = route_node_get(eigrp->networks, p); |
d62a17ae | 215 | if (rn->info) { |
216 | /* There is already same network statement. */ | |
217 | route_unlock_node(rn); | |
218 | return 0; | |
219 | } | |
220 | ||
a96029f8 | 221 | struct prefix *pref = prefix_new(); |
d62a17ae | 222 | PREFIX_COPY_IPV4(pref, p); |
223 | rn->info = (void *)pref; | |
224 | ||
225 | /* Schedule Router ID Update. */ | |
975a328e | 226 | if (eigrp->router_id.s_addr == INADDR_ANY) |
d62a17ae | 227 | eigrp_router_id_update(eigrp); |
228 | /* Run network config now. */ | |
229 | /* Get target interface. */ | |
451fda4f | 230 | FOR_ALL_INTERFACES (vrf, ifp) { |
d62a17ae | 231 | zlog_debug("Setting up %s", ifp->name); |
a96029f8 | 232 | eigrp_network_run_interface(eigrp, p, ifp); |
d62a17ae | 233 | } |
234 | return 1; | |
7f57883e DS |
235 | } |
236 | ||
237 | /* Check whether interface matches given network | |
238 | * returns: 1, true. 0, false | |
239 | */ | |
952248db | 240 | static int eigrp_network_match_iface(const struct prefix *co_prefix, |
d62a17ae | 241 | const struct prefix *net) |
7f57883e | 242 | { |
d62a17ae | 243 | /* new approach: more elegant and conceptually clean */ |
952248db | 244 | return prefix_match_network_statement(net, co_prefix); |
7f57883e DS |
245 | } |
246 | ||
d62a17ae | 247 | static void eigrp_network_run_interface(struct eigrp *eigrp, struct prefix *p, |
248 | struct interface *ifp) | |
7f57883e | 249 | { |
b748db67 | 250 | struct eigrp_interface *ei; |
d62a17ae | 251 | struct listnode *cnode; |
252 | struct connected *co; | |
253 | ||
254 | /* if interface prefix is match specified prefix, | |
255 | then create socket and join multicast group. */ | |
256 | for (ALL_LIST_ELEMENTS_RO(ifp->connected, cnode, co)) { | |
257 | ||
258 | if (CHECK_FLAG(co->flags, ZEBRA_IFA_SECONDARY)) | |
259 | continue; | |
260 | ||
996c9314 | 261 | if (p->family == co->address->family && !ifp->info |
952248db | 262 | && eigrp_network_match_iface(co->address, p)) { |
d62a17ae | 263 | |
264 | ei = eigrp_if_new(eigrp, ifp, co->address); | |
d62a17ae | 265 | |
d62a17ae | 266 | /* Relate eigrp interface to eigrp instance. */ |
267 | ei->eigrp = eigrp; | |
268 | ||
d62a17ae | 269 | /* if router_id is not configured, dont bring up |
270 | * interfaces. | |
271 | * eigrp_router_id_update() will call eigrp_if_update | |
272 | * whenever r-id is configured instead. | |
273 | */ | |
274 | if (if_is_operative(ifp)) | |
275 | eigrp_if_up(ei); | |
276 | } | |
277 | } | |
7f57883e DS |
278 | } |
279 | ||
d62a17ae | 280 | void eigrp_if_update(struct interface *ifp) |
7f57883e | 281 | { |
d62a17ae | 282 | struct listnode *node, *nnode; |
283 | struct route_node *rn; | |
7d7acfc7 | 284 | struct eigrp *eigrp; |
d62a17ae | 285 | |
286 | /* | |
287 | * In the event there are multiple eigrp autonymnous systems running, | |
288 | * we need to check eac one and add the interface as approperate | |
289 | */ | |
290 | for (ALL_LIST_ELEMENTS(eigrp_om->eigrp, node, nnode, eigrp)) { | |
88a3b65e DS |
291 | if (ifp->vrf_id != eigrp->vrf_id) |
292 | continue; | |
293 | ||
d62a17ae | 294 | /* EIGRP must be on and Router-ID must be configured. */ |
3a6290bd | 295 | if (eigrp->router_id.s_addr == INADDR_ANY) |
d62a17ae | 296 | continue; |
297 | ||
298 | /* Run each network for this interface. */ | |
299 | for (rn = route_top(eigrp->networks); rn; rn = route_next(rn)) | |
300 | if (rn->info != NULL) { | |
301 | eigrp_network_run_interface(eigrp, &rn->p, ifp); | |
302 | } | |
303 | } | |
7f57883e DS |
304 | } |
305 | ||
cd6c066e | 306 | int eigrp_network_unset(struct eigrp *eigrp, struct prefix *p) |
7f57883e | 307 | { |
d62a17ae | 308 | struct route_node *rn; |
309 | struct listnode *node, *nnode; | |
310 | struct eigrp_interface *ei; | |
311 | struct prefix *pref; | |
312 | ||
cd6c066e | 313 | rn = route_node_lookup(eigrp->networks, p); |
d62a17ae | 314 | if (rn == NULL) |
315 | return 0; | |
316 | ||
317 | pref = rn->info; | |
318 | route_unlock_node(rn); | |
319 | ||
cd6c066e | 320 | if (!IPV4_ADDR_SAME(&pref->u.prefix4, &p->u.prefix4)) |
d62a17ae | 321 | return 0; |
322 | ||
63265b5c | 323 | prefix_ipv4_free((struct prefix_ipv4 **)&rn->info); |
d62a17ae | 324 | route_unlock_node(rn); /* initial reference */ |
325 | ||
326 | /* Find interfaces that not configured already. */ | |
327 | for (ALL_LIST_ELEMENTS(eigrp->eiflist, node, nnode, ei)) { | |
952248db | 328 | bool found = false; |
d62a17ae | 329 | |
330 | for (rn = route_top(eigrp->networks); rn; rn = route_next(rn)) { | |
331 | if (rn->info == NULL) | |
332 | continue; | |
333 | ||
b245781a | 334 | if (eigrp_network_match_iface(&ei->address, &rn->p)) { |
952248db | 335 | found = true; |
d62a17ae | 336 | route_unlock_node(rn); |
337 | break; | |
338 | } | |
339 | } | |
340 | ||
952248db | 341 | if (!found) { |
d62a17ae | 342 | eigrp_if_free(ei, INTERFACE_DOWN_BY_VTY); |
343 | } | |
344 | } | |
345 | ||
346 | return 1; | |
7f57883e DS |
347 | } |
348 | ||
d62a17ae | 349 | void eigrp_external_routes_refresh(struct eigrp *eigrp, int type) |
7f57883e | 350 | { |
7f57883e | 351 | } |