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