diff options
author | Timo Teräs <timo.teras@iki.fi> | 2017-01-19 16:27:01 +0100 |
---|---|---|
committer | David Lamparter <equinox@opensourcerouting.org> | 2017-03-07 16:20:29 +0100 |
commit | 2fb975da777d27077b3580cb18390b5015b50fb8 (patch) | |
tree | a0877c908b64c4dc1cfb6101e61420007038aeca /nhrpd/netlink_arp.c | |
parent | Merge branch 'frr/pull/255' ("vtysh: Fix cli help string to have only 1 menti... (diff) | |
download | frr-2fb975da777d27077b3580cb18390b5015b50fb8.tar.xz frr-2fb975da777d27077b3580cb18390b5015b50fb8.zip |
nhrpd: implement next hop resolution protocol
This provides DMVPN support and integrates to strongSwan. Please read
README.nhrpd and README.kernel for more details.
[DL: cherry-picked from dafa05e65fe4b3b3ed5525443f554215ba14f42c]
[DL: merge partially resolved, this commit will not build.]
Signed-off-by: Timo Teräs <timo.teras@iki.fi>
Signed-off-by: David Lamparter <equinox@opensourcerouting.org>
Diffstat (limited to 'nhrpd/netlink_arp.c')
-rw-r--r-- | nhrpd/netlink_arp.c | 275 |
1 files changed, 275 insertions, 0 deletions
diff --git a/nhrpd/netlink_arp.c b/nhrpd/netlink_arp.c new file mode 100644 index 000000000..a418ecabd --- /dev/null +++ b/nhrpd/netlink_arp.c @@ -0,0 +1,275 @@ +/* NHRP netlink/neighbor table arpd code + * Copyright (c) 2014-2016 Timo Teräs + * + * This file is free software: you may copy, redistribute and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + */ + +#include <fcntl.h> +#include <net/if.h> +#include <netinet/if_ether.h> +#include <linux/netlink.h> +#include <linux/neighbour.h> +#include <linux/netfilter/nfnetlink_log.h> + +#include "thread.h" +#include "nhrpd.h" +#include "netlink.h" +#include "znl.h" + +int netlink_req_fd = -1; +int netlink_nflog_group; +static int netlink_log_fd = -1; +static struct thread *netlink_log_thread; +static int netlink_listen_fd = -1; + +typedef void (*netlink_dispatch_f)(struct nlmsghdr *msg, struct zbuf *zb); + +void netlink_update_binding(struct interface *ifp, union sockunion *proto, union sockunion *nbma) +{ + struct nlmsghdr *n; + struct ndmsg *ndm; + struct zbuf *zb = zbuf_alloc(512); + + n = znl_nlmsg_push(zb, nbma ? RTM_NEWNEIGH : RTM_DELNEIGH, NLM_F_REQUEST | NLM_F_REPLACE | NLM_F_CREATE); + ndm = znl_push(zb, sizeof(*ndm)); + *ndm = (struct ndmsg) { + .ndm_family = sockunion_family(proto), + .ndm_ifindex = ifp->ifindex, + .ndm_type = RTN_UNICAST, + .ndm_state = nbma ? NUD_REACHABLE : NUD_FAILED, + }; + znl_rta_push(zb, NDA_DST, sockunion_get_addr(proto), family2addrsize(sockunion_family(proto))); + if (nbma) + znl_rta_push(zb, NDA_LLADDR, sockunion_get_addr(nbma), family2addrsize(sockunion_family(nbma))); + znl_nlmsg_complete(zb, n); + zbuf_send(zb, netlink_req_fd); + zbuf_recv(zb, netlink_req_fd); + zbuf_free(zb); +} + +static void netlink_neigh_msg(struct nlmsghdr *msg, struct zbuf *zb) +{ + struct ndmsg *ndm; + struct rtattr *rta; + struct nhrp_cache *c; + struct interface *ifp; + struct zbuf payload; + union sockunion addr; + size_t len; + char buf[SU_ADDRSTRLEN]; + int state; + + ndm = znl_pull(zb, sizeof(*ndm)); + if (!ndm) return; + + sockunion_family(&addr) = AF_UNSPEC; + while ((rta = znl_rta_pull(zb, &payload)) != NULL) { + len = zbuf_used(&payload); + switch (rta->rta_type) { + case NDA_DST: + sockunion_set(&addr, ndm->ndm_family, zbuf_pulln(&payload, len), len); + break; + } + } + + ifp = if_lookup_by_index(ndm->ndm_ifindex); + if (!ifp || sockunion_family(&addr) == AF_UNSPEC) + return; + + c = nhrp_cache_get(ifp, &addr, 0); + if (!c) + return; + + if (msg->nlmsg_type == RTM_GETNEIGH) { + debugf(NHRP_DEBUG_KERNEL, "Netlink: who-has %s dev %s", + sockunion2str(&addr, buf, sizeof buf), + ifp->name); + + if (c->cur.type >= NHRP_CACHE_CACHED) { + nhrp_cache_set_used(c, 1); + netlink_update_binding(ifp, &addr, &c->cur.peer->vc->remote.nbma); + } + } else { + debugf(NHRP_DEBUG_KERNEL, "Netlink: update %s dev %s nud %x", + sockunion2str(&addr, buf, sizeof buf), + ifp->name, ndm->ndm_state); + + state = (msg->nlmsg_type == RTM_NEWNEIGH) ? ndm->ndm_state : NUD_FAILED; + nhrp_cache_set_used(c, state == NUD_REACHABLE); + } +} + +static int netlink_route_recv(struct thread *t) +{ + uint8_t buf[ZNL_BUFFER_SIZE]; + int fd = THREAD_FD(t); + struct zbuf payload, zb; + struct nlmsghdr *n; + + zbuf_init(&zb, buf, sizeof(buf), 0); + while (zbuf_recv(&zb, fd) > 0) { + while ((n = znl_nlmsg_pull(&zb, &payload)) != 0) { + debugf(NHRP_DEBUG_KERNEL, "Netlink: Received msg_type %u, msg_flags %u", + n->nlmsg_type, n->nlmsg_flags); + switch (n->nlmsg_type) { + case RTM_GETNEIGH: + case RTM_NEWNEIGH: + case RTM_DELNEIGH: + netlink_neigh_msg(n, &payload); + break; + } + } + } + + thread_add_read(master, netlink_route_recv, 0, fd); + + return 0; +} + +static void netlink_log_register(int fd, int group) +{ + struct nlmsghdr *n; + struct nfgenmsg *nf; + struct nfulnl_msg_config_cmd cmd; + struct zbuf *zb = zbuf_alloc(512); + + n = znl_nlmsg_push(zb, (NFNL_SUBSYS_ULOG<<8) | NFULNL_MSG_CONFIG, NLM_F_REQUEST | NLM_F_ACK); + nf = znl_push(zb, sizeof(*nf)); + *nf = (struct nfgenmsg) { + .nfgen_family = AF_UNSPEC, + .version = NFNETLINK_V0, + .res_id = htons(group), + }; + cmd.command = NFULNL_CFG_CMD_BIND; + znl_rta_push(zb, NFULA_CFG_CMD, &cmd, sizeof(cmd)); + znl_nlmsg_complete(zb, n); + + zbuf_send(zb, fd); + zbuf_free(zb); +} + +static void netlink_log_indication(struct nlmsghdr *msg, struct zbuf *zb) +{ + struct nfgenmsg *nf; + struct rtattr *rta; + struct zbuf rtapl, pktpl; + struct interface *ifp; + struct nfulnl_msg_packet_hdr *pkthdr = NULL; + uint32_t *in_ndx = NULL; + + nf = znl_pull(zb, sizeof(*nf)); + if (!nf) return; + + memset(&pktpl, 0, sizeof(pktpl)); + while ((rta = znl_rta_pull(zb, &rtapl)) != NULL) { + switch (rta->rta_type) { + case NFULA_PACKET_HDR: + pkthdr = znl_pull(&rtapl, sizeof(*pkthdr)); + break; + case NFULA_IFINDEX_INDEV: + in_ndx = znl_pull(&rtapl, sizeof(*in_ndx)); + break; + case NFULA_PAYLOAD: + pktpl = rtapl; + break; + /* NFULA_HWHDR exists and is supposed to contain source + * hardware address. However, for ip_gre it seems to be + * the nexthop destination address if the packet matches + * route. */ + } + } + + if (!pkthdr || !in_ndx || !zbuf_used(&pktpl)) + return; + + ifp = if_lookup_by_index(htonl(*in_ndx)); + if (!ifp) + return; + + nhrp_peer_send_indication(ifp, htons(pkthdr->hw_protocol), &pktpl); +} + +static int netlink_log_recv(struct thread *t) +{ + uint8_t buf[ZNL_BUFFER_SIZE]; + int fd = THREAD_FD(t); + struct zbuf payload, zb; + struct nlmsghdr *n; + + netlink_log_thread = NULL; + + zbuf_init(&zb, buf, sizeof(buf), 0); + while (zbuf_recv(&zb, fd) > 0) { + while ((n = znl_nlmsg_pull(&zb, &payload)) != 0) { + debugf(NHRP_DEBUG_KERNEL, "Netlink-log: Received msg_type %u, msg_flags %u", + n->nlmsg_type, n->nlmsg_flags); + switch (n->nlmsg_type) { + case (NFNL_SUBSYS_ULOG<<8) | NFULNL_MSG_PACKET: + netlink_log_indication(n, &payload); + break; + } + } + } + + THREAD_READ_ON(master, netlink_log_thread, netlink_log_recv, 0, netlink_log_fd); + + return 0; +} + +void netlink_set_nflog_group(int nlgroup) +{ + if (netlink_log_fd >= 0) { + THREAD_OFF(netlink_log_thread); + close(netlink_log_fd); + netlink_log_fd = -1; + } + netlink_nflog_group = nlgroup; + if (nlgroup) { + netlink_log_fd = znl_open(NETLINK_NETFILTER, 0); + netlink_log_register(netlink_log_fd, nlgroup); + THREAD_READ_ON(master, netlink_log_thread, netlink_log_recv, 0, netlink_log_fd); + } +} + +int netlink_init(void) +{ + netlink_req_fd = znl_open(NETLINK_ROUTE, 0); + netlink_listen_fd = znl_open(NETLINK_ROUTE, RTMGRP_NEIGH); + thread_add_read(master, netlink_route_recv, 0, netlink_listen_fd); + + return 0; +} + +int netlink_configure_arp(unsigned int ifindex, int pf) +{ + struct nlmsghdr *n; + struct ndtmsg *ndtm; + struct rtattr *rta; + struct zbuf *zb = zbuf_alloc(512); + int r; + + n = znl_nlmsg_push(zb, RTM_SETNEIGHTBL, NLM_F_REQUEST | NLM_F_REPLACE); + ndtm = znl_push(zb, sizeof(*ndtm)); + *ndtm = (struct ndtmsg) { + .ndtm_family = pf, + }; + + znl_rta_push(zb, NDTA_NAME, pf == AF_INET ? "arp_cache" : "ndisc_cache", 10); + + rta = znl_rta_nested_push(zb, NDTA_PARMS); + znl_rta_push_u32(zb, NDTPA_IFINDEX, ifindex); + znl_rta_push_u32(zb, NDTPA_APP_PROBES, 1); + znl_rta_push_u32(zb, NDTPA_MCAST_PROBES, 0); + znl_rta_push_u32(zb, NDTPA_UCAST_PROBES, 0); + znl_rta_nested_complete(zb, rta); + + znl_nlmsg_complete(zb, n); + r = zbuf_send(zb, netlink_req_fd); + zbuf_recv(zb, netlink_req_fd); + zbuf_free(zb); + + return r; +} |