]>
Commit | Line | Data |
---|---|---|
acddc0ed | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
718e3744 | 2 | /* |
3 | * OSPF network related functions | |
4 | * Copyright (C) 1999 Toshiaki Takada | |
718e3744 | 5 | */ |
6 | ||
7 | #include <zebra.h> | |
8 | ||
24a58196 | 9 | #include "frrevent.h" |
718e3744 | 10 | #include "linklist.h" |
11 | #include "prefix.h" | |
12 | #include "if.h" | |
13 | #include "sockunion.h" | |
14 | #include "log.h" | |
15 | #include "sockopt.h" | |
edd7c245 | 16 | #include "privs.h" |
313d7993 | 17 | #include "lib_errors.h" |
04a0401f | 18 | #include "lib/table.h" |
edd7c245 | 19 | |
718e3744 | 20 | #include "ospfd/ospfd.h" |
21 | #include "ospfd/ospf_network.h" | |
22 | #include "ospfd/ospf_interface.h" | |
23 | #include "ospfd/ospf_asbr.h" | |
24 | #include "ospfd/ospf_lsa.h" | |
25 | #include "ospfd/ospf_lsdb.h" | |
26 | #include "ospfd/ospf_neighbor.h" | |
27 | #include "ospfd/ospf_packet.h" | |
05ba78e4 | 28 | #include "ospfd/ospf_dump.h" |
edd7c245 | 29 | |
718e3744 | 30 | /* Join to the OSPF ALL SPF ROUTERS multicast group. */ |
d62a17ae | 31 | int ospf_if_add_allspfrouters(struct ospf *top, struct prefix *p, |
32 | ifindex_t ifindex) | |
718e3744 | 33 | { |
d62a17ae | 34 | int ret; |
35 | ||
36 | ret = setsockopt_ipv4_multicast(top->fd, IP_ADD_MEMBERSHIP, | |
37 | p->u.prefix4, htonl(OSPF_ALLSPFROUTERS), | |
38 | ifindex); | |
39 | if (ret < 0) | |
ade6974d | 40 | flog_err( |
450971aa | 41 | EC_LIB_SOCKET, |
96b663a3 MS |
42 | "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?", |
43 | top->fd, &p->u.prefix4, ifindex, | |
ade6974d | 44 | safe_strerror(errno)); |
05ba78e4 CS |
45 | else { |
46 | if (IS_DEBUG_OSPF_EVENT) | |
996c9314 | 47 | zlog_debug( |
96b663a3 MS |
48 | "interface %pI4 [%u] join AllSPFRouters Multicast group.", |
49 | &p->u.prefix4, ifindex); | |
05ba78e4 | 50 | } |
d62a17ae | 51 | |
52 | return ret; | |
718e3744 | 53 | } |
54 | ||
d62a17ae | 55 | int ospf_if_drop_allspfrouters(struct ospf *top, struct prefix *p, |
56 | ifindex_t ifindex) | |
718e3744 | 57 | { |
d62a17ae | 58 | int ret; |
59 | ||
60 | ret = setsockopt_ipv4_multicast(top->fd, IP_DROP_MEMBERSHIP, | |
61 | p->u.prefix4, htonl(OSPF_ALLSPFROUTERS), | |
62 | ifindex); | |
63 | if (ret < 0) | |
450971aa | 64 | flog_err(EC_LIB_SOCKET, |
96b663a3 MS |
65 | "can't setsockopt IP_DROP_MEMBERSHIP (fd %d, addr %pI4, ifindex %u, AllSPFRouters): %s", |
66 | top->fd, &p->u.prefix4, ifindex, | |
abcc171c | 67 | safe_strerror(errno)); |
05ba78e4 CS |
68 | else { |
69 | if (IS_DEBUG_OSPF_EVENT) | |
996c9314 | 70 | zlog_debug( |
96b663a3 MS |
71 | "interface %pI4 [%u] leave AllSPFRouters Multicast group.", |
72 | &p->u.prefix4, ifindex); | |
05ba78e4 | 73 | } |
d62a17ae | 74 | |
75 | return ret; | |
718e3744 | 76 | } |
77 | ||
78 | /* Join to the OSPF ALL Designated ROUTERS multicast group. */ | |
d62a17ae | 79 | int ospf_if_add_alldrouters(struct ospf *top, struct prefix *p, |
80 | ifindex_t ifindex) | |
718e3744 | 81 | { |
d62a17ae | 82 | int ret; |
83 | ||
84 | ret = setsockopt_ipv4_multicast(top->fd, IP_ADD_MEMBERSHIP, | |
85 | p->u.prefix4, htonl(OSPF_ALLDROUTERS), | |
86 | ifindex); | |
87 | if (ret < 0) | |
ade6974d | 88 | flog_err( |
450971aa | 89 | EC_LIB_SOCKET, |
96b663a3 MS |
90 | "can't setsockopt IP_ADD_MEMBERSHIP (fd %d, addr %pI4, ifindex %u, AllDRouters): %s; perhaps a kernel limit on # of multicast group memberships has been exceeded?", |
91 | top->fd, &p->u.prefix4, ifindex, | |
ade6974d | 92 | safe_strerror(errno)); |
15e78e64 DS |
93 | else { |
94 | if (IS_DEBUG_OSPF_EVENT) | |
95 | zlog_debug( | |
96 | "interface %pI4 [%u] join AllDRouters Multicast group.", | |
97 | &p->u.prefix4, ifindex); | |
98 | } | |
d62a17ae | 99 | return ret; |
718e3744 | 100 | } |
101 | ||
d62a17ae | 102 | int ospf_if_drop_alldrouters(struct ospf *top, struct prefix *p, |
103 | ifindex_t ifindex) | |
718e3744 | 104 | { |
d62a17ae | 105 | int ret; |
106 | ||
107 | ret = setsockopt_ipv4_multicast(top->fd, IP_DROP_MEMBERSHIP, | |
108 | p->u.prefix4, htonl(OSPF_ALLDROUTERS), | |
109 | ifindex); | |
110 | if (ret < 0) | |
450971aa | 111 | flog_err(EC_LIB_SOCKET, |
96b663a3 MS |
112 | "can't setsockopt IP_DROP_MEMBERSHIP (fd %d, addr %pI4, ifindex %u, AllDRouters): %s", |
113 | top->fd, &p->u.prefix4, ifindex, | |
abcc171c | 114 | safe_strerror(errno)); |
6e6e1020 | 115 | else if (IS_DEBUG_OSPF_EVENT) |
d62a17ae | 116 | zlog_debug( |
96b663a3 MS |
117 | "interface %pI4 [%u] leave AllDRouters Multicast group.", |
118 | &p->u.prefix4, ifindex); | |
d62a17ae | 119 | |
120 | return ret; | |
718e3744 | 121 | } |
122 | ||
04a0401f | 123 | int ospf_if_ipmulticast(int fd, struct prefix *p, ifindex_t ifindex) |
718e3744 | 124 | { |
d7c0a89a | 125 | uint8_t val; |
d62a17ae | 126 | int ret, len; |
127 | ||
128 | /* Prevent receiving self-origined multicast packets. */ | |
04a0401f | 129 | ret = setsockopt_ipv4_multicast_loop(fd, 0); |
d62a17ae | 130 | if (ret < 0) |
450971aa | 131 | flog_err(EC_LIB_SOCKET, |
abcc171c | 132 | "can't setsockopt IP_MULTICAST_LOOP(0) for fd %d: %s", |
04a0401f | 133 | fd, safe_strerror(errno)); |
d62a17ae | 134 | |
135 | /* Explicitly set multicast ttl to 1 -- endo. */ | |
136 | val = 1; | |
137 | len = sizeof(val); | |
04a0401f | 138 | ret = setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, (void *)&val, len); |
d62a17ae | 139 | if (ret < 0) |
450971aa | 140 | flog_err(EC_LIB_SOCKET, |
abcc171c | 141 | "can't setsockopt IP_MULTICAST_TTL(1) for fd %d: %s", |
04a0401f | 142 | fd, safe_strerror(errno)); |
e1b18df1 CS |
143 | #ifndef GNU_LINUX |
144 | /* For GNU LINUX ospf_write uses IP_PKTINFO, in_pktinfo to send | |
145 | * packet out of ifindex. Below would be used Non Linux system. | |
146 | */ | |
04a0401f | 147 | ret = setsockopt_ipv4_multicast_if(fd, p->u.prefix4, ifindex); |
e1b18df1 | 148 | if (ret < 0) |
450971aa | 149 | flog_err(EC_LIB_SOCKET, |
96b663a3 | 150 | "can't setsockopt IP_MULTICAST_IF(fd %d, addr %pI4, ifindex %u): %s", |
04a0401f | 151 | fd, &p->u.prefix4, ifindex, |
abcc171c | 152 | safe_strerror(errno)); |
e1b18df1 | 153 | #endif |
d62a17ae | 154 | |
e7503eab CS |
155 | return ret; |
156 | } | |
157 | ||
04a0401f MS |
158 | /* |
159 | * Helper to open and set up a socket; returns the new fd on success, | |
160 | * -1 on error. | |
161 | */ | |
162 | static int sock_init_common(vrf_id_t vrf_id, const char *name, int *pfd) | |
718e3744 | 163 | { |
d62a17ae | 164 | int ospf_sock; |
165 | int ret, hincl = 1; | |
d62a17ae | 166 | |
04a0401f | 167 | if (vrf_id == VRF_UNKNOWN) { |
3c0eb8fa PG |
168 | /* silently return since VRF is not ready */ |
169 | return -1; | |
170 | } | |
04a0401f | 171 | |
0cf6db21 | 172 | frr_with_privs(&ospfd_privs) { |
6bb30c2c | 173 | ospf_sock = vrf_socket(AF_INET, SOCK_RAW, IPPROTO_OSPFIGP, |
04a0401f | 174 | vrf_id, name); |
6bb30c2c | 175 | if (ospf_sock < 0) { |
04a0401f | 176 | flog_err(EC_LIB_SOCKET, "%s: socket: %s", __func__, |
6bb30c2c | 177 | safe_strerror(errno)); |
95d7a42a | 178 | return -1; |
6bb30c2c | 179 | } |
d62a17ae | 180 | |
5bd4189c | 181 | #ifdef IP_HDRINCL |
6bb30c2c DL |
182 | /* we will include IP header with packet */ |
183 | ret = setsockopt(ospf_sock, IPPROTO_IP, IP_HDRINCL, &hincl, | |
184 | sizeof(hincl)); | |
185 | if (ret < 0) { | |
450971aa | 186 | flog_err(EC_LIB_SOCKET, |
abcc171c DS |
187 | "Can't set IP_HDRINCL option for fd %d: %s", |
188 | ospf_sock, safe_strerror(errno)); | |
6bb30c2c DL |
189 | break; |
190 | } | |
d62a17ae | 191 | #elif defined(IPTOS_PREC_INTERNETCONTROL) |
5bd4189c | 192 | #warning "IP_HDRINCL not available on this system" |
193 | #warning "using IPTOS_PREC_INTERNETCONTROL" | |
6bb30c2c DL |
194 | ret = setsockopt_ipv4_tos(ospf_sock, |
195 | IPTOS_PREC_INTERNETCONTROL); | |
196 | if (ret < 0) { | |
450971aa | 197 | flog_err(EC_LIB_SOCKET, |
abcc171c DS |
198 | "can't set sockopt IP_TOS %d to socket %d: %s", |
199 | tos, ospf_sock, safe_strerror(errno)); | |
6bb30c2c DL |
200 | break; |
201 | } | |
5bd4189c | 202 | #else /* !IPTOS_PREC_INTERNETCONTROL */ |
203 | #warning "IP_HDRINCL not available, nor is IPTOS_PREC_INTERNETCONTROL" | |
1c50c1c0 | 204 | flog_err(EC_LIB_UNAVAILABLE, "IP_HDRINCL option not available"); |
5bd4189c | 205 | #endif /* IP_HDRINCL */ |
718e3744 | 206 | |
6bb30c2c | 207 | ret = setsockopt_ifindex(AF_INET, ospf_sock, 1); |
ac191232 | 208 | |
6bb30c2c | 209 | if (ret < 0) |
450971aa | 210 | flog_err(EC_LIB_SOCKET, |
abcc171c DS |
211 | "Can't set pktinfo option for fd %d", |
212 | ospf_sock); | |
6bb30c2c | 213 | } |
e7503eab | 214 | |
04a0401f | 215 | *pfd = ospf_sock; |
338b8e91 | 216 | |
e7503eab | 217 | return ret; |
718e3744 | 218 | } |
6e6e1020 MS |
219 | |
220 | /* | |
221 | * Update a socket bufsize(s), based on its ospf instance | |
222 | */ | |
223 | void ospf_sock_bufsize_update(const struct ospf *ospf, int sock, | |
224 | enum ospf_sock_type_e type) | |
225 | { | |
226 | int bufsize; | |
227 | ||
228 | if (type == OSPF_SOCK_BOTH || type == OSPF_SOCK_RECV) { | |
229 | bufsize = ospf->recv_sock_bufsize; | |
230 | setsockopt_so_recvbuf(sock, bufsize); | |
231 | } | |
232 | ||
233 | if (type == OSPF_SOCK_BOTH || type == OSPF_SOCK_SEND) { | |
234 | bufsize = ospf->send_sock_bufsize; | |
235 | setsockopt_so_sendbuf(sock, bufsize); | |
236 | } | |
237 | } | |
04a0401f MS |
238 | |
239 | int ospf_sock_init(struct ospf *ospf) | |
240 | { | |
241 | int ret; | |
242 | ||
243 | /* silently ignore. already done */ | |
244 | if (ospf->fd > 0) | |
245 | return -1; | |
246 | ||
247 | ret = sock_init_common(ospf->vrf_id, ospf->name, &(ospf->fd)); | |
248 | ||
249 | if (ret >= 0) /* Update socket buffer sizes */ | |
250 | ospf_sock_bufsize_update(ospf, ospf->fd, OSPF_SOCK_BOTH); | |
251 | ||
252 | return ret; | |
253 | } | |
254 | ||
255 | /* | |
256 | * Open per-interface write socket | |
257 | */ | |
258 | int ospf_ifp_sock_init(struct interface *ifp) | |
259 | { | |
260 | struct ospf_if_info *oii; | |
261 | struct ospf_interface *oi; | |
262 | struct ospf *ospf; | |
263 | struct route_node *rn; | |
264 | int ret; | |
265 | ||
266 | oii = IF_OSPF_IF_INFO(ifp); | |
267 | if (oii == NULL) | |
268 | return -1; | |
269 | ||
270 | if (oii->oii_fd > 0) | |
271 | return 0; | |
272 | ||
273 | rn = route_top(IF_OIFS(ifp)); | |
274 | if (rn && rn->info) { | |
275 | oi = rn->info; | |
276 | ospf = oi->ospf; | |
277 | } else | |
278 | return -1; | |
279 | ||
280 | ret = sock_init_common(ifp->vrf->vrf_id, ifp->name, &oii->oii_fd); | |
281 | ||
282 | if (ret >= 0) /* Update socket buffer sizes */ | |
283 | ospf_sock_bufsize_update(ospf, oii->oii_fd, OSPF_SOCK_BOTH); | |
284 | ||
285 | if (IS_DEBUG_OSPF_EVENT) | |
286 | zlog_debug("%s: ifp %s, oii %p, fd %d", __func__, ifp->name, | |
287 | oii, oii->oii_fd); | |
288 | ||
289 | return ret; | |
290 | } | |
291 | ||
292 | /* | |
293 | * Close per-interface write socket | |
294 | */ | |
295 | int ospf_ifp_sock_close(struct interface *ifp) | |
296 | { | |
297 | struct ospf_if_info *oii; | |
298 | ||
299 | oii = IF_OSPF_IF_INFO(ifp); | |
300 | if (oii == NULL) | |
301 | return 0; | |
302 | ||
303 | if (oii->oii_fd > 0) { | |
304 | if (IS_DEBUG_OSPF_EVENT) | |
305 | zlog_debug("%s: ifp %s, oii %p, fd %d", __func__, | |
306 | ifp->name, oii, oii->oii_fd); | |
307 | ||
308 | close(oii->oii_fd); | |
309 | oii->oii_fd = -1; | |
310 | } | |
311 | ||
312 | return 0; | |
313 | } |