diff options
Diffstat (limited to '')
-rw-r--r-- | eigrpd/eigrp_network.c | 351 |
1 files changed, 351 insertions, 0 deletions
diff --git a/eigrpd/eigrp_network.c b/eigrpd/eigrp_network.c new file mode 100644 index 0000000..13db38c --- /dev/null +++ b/eigrpd/eigrp_network.c @@ -0,0 +1,351 @@ +/* + * EIGRP Network Related Functions. + * Copyright (C) 2013-2014 + * Authors: + * Donnie Savage + * Jan Janovic + * Matej Perina + * Peter Orsag + * Peter Paluch + * + * This file is part of GNU Zebra. + * + * GNU Zebra is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * GNU Zebra is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <zebra.h> + +#include "thread.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; +} + +/* 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 listnode *cnode; + struct connected *co; + + /* if interface prefix is match specified prefix, + then create socket and join multicast group. */ + for (ALL_LIST_ELEMENTS_RO(ifp->connected, cnode, 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 listnode *node, *nnode; + 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 + */ + for (ALL_LIST_ELEMENTS(eigrp_om->eigrp, node, nnode, 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 listnode *node, *nnode; + 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. */ + for (ALL_LIST_ELEMENTS(eigrp->eiflist, node, nnode, 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) +{ +} |