| /* NHRP netlink/GRE tunnel configuration 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 <sys/socket.h> |
| #include <linux/netlink.h> |
| #include <linux/rtnetlink.h> |
| #include <linux/in.h> |
| #include <linux/if.h> |
| #include <linux/ip.h> |
| #include <linux/ipv6.h> |
| #include <linux/if_tunnel.h> |
| |
| #include "debug.h" |
| #include "netlink.h" |
| #include "znl.h" |
| |
| static int __netlink_gre_get_data(struct zbuf *zb, struct zbuf *data, int ifindex) |
| { |
| struct nlmsghdr *n; |
| struct ifinfomsg *ifi; |
| struct zbuf payload, rtapayload; |
| struct rtattr *rta; |
| |
| debugf(NHRP_DEBUG_KERNEL, "netlink-link-gre: get-info %u", ifindex); |
| |
| n = znl_nlmsg_push(zb, RTM_GETLINK, NLM_F_REQUEST); |
| ifi = znl_push(zb, sizeof(*ifi)); |
| *ifi = (struct ifinfomsg) { |
| .ifi_index = ifindex, |
| }; |
| znl_nlmsg_complete(zb, n); |
| |
| if (zbuf_send(zb, netlink_req_fd) < 0 || |
| zbuf_recv(zb, netlink_req_fd) < 0) |
| return -1; |
| |
| n = znl_nlmsg_pull(zb, &payload); |
| if (!n) return -1; |
| |
| if (n->nlmsg_type != RTM_NEWLINK) |
| return -1; |
| |
| ifi = znl_pull(&payload, sizeof(struct ifinfomsg)); |
| if (!ifi) |
| return -1; |
| |
| debugf(NHRP_DEBUG_KERNEL, "netlink-link-gre: ifindex %u, receive msg_type %u, msg_flags %u", |
| ifi->ifi_index, n->nlmsg_type, n->nlmsg_flags); |
| |
| if (ifi->ifi_index != ifindex) |
| return -1; |
| |
| while ((rta = znl_rta_pull(&payload, &rtapayload)) != NULL) |
| if (rta->rta_type == IFLA_LINKINFO) |
| break; |
| if (!rta) return -1; |
| |
| payload = rtapayload; |
| while ((rta = znl_rta_pull(&payload, &rtapayload)) != NULL) |
| if (rta->rta_type == IFLA_INFO_DATA) |
| break; |
| if (!rta) return -1; |
| |
| *data = rtapayload; |
| return 0; |
| } |
| |
| void netlink_gre_get_info(unsigned int ifindex, uint32_t *gre_key, unsigned int *link_index, struct in_addr *saddr) |
| { |
| struct zbuf *zb = zbuf_alloc(8192), data, rtapl; |
| struct rtattr *rta; |
| |
| *link_index = 0; |
| *gre_key = 0; |
| saddr->s_addr = 0; |
| |
| if (__netlink_gre_get_data(zb, &data, ifindex) < 0) |
| goto err; |
| |
| while ((rta = znl_rta_pull(&data, &rtapl)) != NULL) { |
| switch (rta->rta_type) { |
| case IFLA_GRE_LINK: |
| *link_index = zbuf_get32(&rtapl); |
| break; |
| case IFLA_GRE_IKEY: |
| case IFLA_GRE_OKEY: |
| *gre_key = zbuf_get32(&rtapl); |
| break; |
| case IFLA_GRE_LOCAL: |
| saddr->s_addr = zbuf_get32(&rtapl); |
| break; |
| } |
| } |
| err: |
| zbuf_free(zb); |
| } |
| |
| void netlink_gre_set_link(unsigned int ifindex, unsigned int link_index) |
| { |
| struct nlmsghdr *n; |
| struct ifinfomsg *ifi; |
| struct rtattr *rta_info, *rta_data, *rta; |
| struct zbuf *zr = zbuf_alloc(8192), data, rtapl; |
| struct zbuf *zb = zbuf_alloc(8192); |
| size_t len; |
| |
| if (__netlink_gre_get_data(zr, &data, ifindex) < 0) |
| goto err; |
| |
| n = znl_nlmsg_push(zb, RTM_NEWLINK, NLM_F_REQUEST); |
| ifi = znl_push(zb, sizeof(*ifi)); |
| *ifi = (struct ifinfomsg) { |
| .ifi_index = ifindex, |
| }; |
| rta_info = znl_rta_nested_push(zb, IFLA_LINKINFO); |
| znl_rta_push(zb, IFLA_INFO_KIND, "gre", 3); |
| rta_data = znl_rta_nested_push(zb, IFLA_INFO_DATA); |
| |
| znl_rta_push_u32(zb, IFLA_GRE_LINK, link_index); |
| while ((rta = znl_rta_pull(&data, &rtapl)) != NULL) { |
| if (rta->rta_type == IFLA_GRE_LINK) |
| continue; |
| len = zbuf_used(&rtapl); |
| znl_rta_push(zb, rta->rta_type, zbuf_pulln(&rtapl, len), len); |
| } |
| |
| znl_rta_nested_complete(zb, rta_data); |
| znl_rta_nested_complete(zb, rta_info); |
| |
| znl_nlmsg_complete(zb, n); |
| zbuf_send(zb, netlink_req_fd); |
| zbuf_recv(zb, netlink_req_fd); |
| err: |
| zbuf_free(zb); |
| zbuf_free(zr); |
| } |