-/* The arguments are marked as unused to prevent warnings on platforms where
- * the Netlink interface isn't supported. */
-static int
-setup_gre_netlink(const char *name UNUSED, struct gre_config *config UNUSED,
- bool create UNUSED)
-{
-#ifdef GRE_IOCTL_ONLY
- return EOPNOTSUPP;
-#else
- int error;
- struct ofpbuf request, *reply;
- unsigned int nl_flags;
- struct ifinfomsg ifinfomsg;
- struct nlattr *linkinfo_hdr;
- struct nlattr *info_data_hdr;
- uint16_t iflags = 0;
- uint16_t oflags = 0;
- uint8_t pmtudisc = 0;
-
- VLOG_DBG("%s: attempting to create gre device using netlink", name);
-
- if (!gre_descriptors.nl_sock) {
- error = nl_sock_create(NETLINK_ROUTE, 0, 0, 0,
- &gre_descriptors.nl_sock);
- if (error) {
- VLOG_WARN("couldn't create netlink socket: %s", strerror(error));
- goto error;
- }
- }
-
- ofpbuf_init(&request, 0);
-
- nl_flags = NLM_F_REQUEST;
- if (create) {
- nl_flags |= NLM_F_CREATE|NLM_F_EXCL;
- }
-
- /* We over-reserve space, because we do some pointer arithmetic
- * and don't want the buffer address shifting under us. */
- nl_msg_put_nlmsghdr(&request, gre_descriptors.nl_sock, 2048, RTM_NEWLINK,
- nl_flags);
-
- memset(&ifinfomsg, 0, sizeof ifinfomsg);
- ifinfomsg.ifi_family = AF_UNSPEC;
- nl_msg_put(&request, &ifinfomsg, sizeof ifinfomsg);
-
- linkinfo_hdr = ofpbuf_tail(&request);
- nl_msg_put_unspec(&request, IFLA_LINKINFO, NULL, 0);
-
- nl_msg_put_unspec(&request, IFLA_INFO_KIND, "gretap", 6);
-
- info_data_hdr = ofpbuf_tail(&request);
- nl_msg_put_unspec(&request, IFLA_INFO_DATA, NULL, 0);
-
- /* Set flags */
- if (config->have_in_key) {
- iflags |= GRE_KEY;
- }
- if (config->have_out_key) {
- oflags |= GRE_KEY;
- }
-
- if (config->in_csum) {
- iflags |= GRE_CSUM;
- }
- if (config->out_csum) {
- oflags |= GRE_CSUM;
- }
-
- /* Add options */
- nl_msg_put_u32(&request, IFLA_GRE_IKEY, config->in_key);
- nl_msg_put_u32(&request, IFLA_GRE_OKEY, config->out_key);
- nl_msg_put_u16(&request, IFLA_GRE_IFLAGS, iflags);
- nl_msg_put_u16(&request, IFLA_GRE_OFLAGS, oflags);
- nl_msg_put_u32(&request, IFLA_GRE_LOCAL, config->local_ip);
- nl_msg_put_u32(&request, IFLA_GRE_REMOTE, config->remote_ip);
- nl_msg_put_u8(&request, IFLA_GRE_PMTUDISC, pmtudisc);
- nl_msg_put_u8(&request, IFLA_GRE_TTL, 0);
- nl_msg_put_u8(&request, IFLA_GRE_TOS, 0);
-
- info_data_hdr->nla_len = (char *)ofpbuf_tail(&request)
- - (char *)info_data_hdr;
- linkinfo_hdr->nla_len = (char *)ofpbuf_tail(&request)
- - (char *)linkinfo_hdr;
-
- nl_msg_put_string(&request, IFLA_IFNAME, name);
-
- error = nl_sock_transact(gre_descriptors.nl_sock, &request, &reply);
- ofpbuf_uninit(&request);
- if (error) {
- VLOG_WARN("couldn't transact netlink socket: %s", strerror(error));
- goto error;
- }
- ofpbuf_delete(reply);
-
-error:
- return error;
-#endif
-}
-
-static int
-setup_gre_ioctl(const char *name, struct gre_config *config, bool create)
-{
- struct ip_tunnel_parm p;
- struct ifreq ifr;
-
- VLOG_DBG("%s: attempting to create gre device using ioctl", name);
-
- memset(&p, 0, sizeof p);
-
- strncpy(p.name, name, IFNAMSIZ);
-
- p.iph.version = 4;
- p.iph.ihl = 5;
- p.iph.protocol = IPPROTO_GRE;
- p.iph.saddr = config->local_ip;
- p.iph.daddr = config->remote_ip;
-
- if (config->have_in_key) {
- p.i_flags |= GRE_KEY;
- p.i_key = config->in_key;
- }
- if (config->have_out_key) {
- p.o_flags |= GRE_KEY;
- p.o_key = config->out_key;
- }
-
- if (config->in_csum) {
- p.i_flags |= GRE_CSUM;
- }
- if (config->out_csum) {
- p.o_flags |= GRE_CSUM;
- }
-
- strncpy(ifr.ifr_name, create ? GRE_IOCTL_DEVICE : name, IFNAMSIZ);
- ifr.ifr_ifru.ifru_data = (void *)&p;
-
- if (!gre_descriptors.ioctl_fd) {
- gre_descriptors.ioctl_fd = socket(AF_INET, SOCK_DGRAM, 0);
- if (gre_descriptors.ioctl_fd < 0) {
- VLOG_WARN("couldn't create gre ioctl socket: %s", strerror(errno));
- gre_descriptors.ioctl_fd = 0;
- return errno;
- }
- }
-
- if (ioctl(gre_descriptors.ioctl_fd, create ? SIOCADDGRETAP : SIOCCHGGRETAP,
- &ifr) < 0) {
- VLOG_WARN("couldn't do gre ioctl: %s", strerror(errno));
- return errno;
- }
-
- return 0;
-}
-
-/* The arguments are marked as unused to prevent warnings on platforms where
- * the Netlink interface isn't supported. */
-static bool
-check_gre_device_netlink(const char *name UNUSED)
-{
-#ifdef GRE_IOCTL_ONLY
- return false;
-#else
- static const struct nl_policy getlink_policy[] = {
- [IFLA_LINKINFO] = { .type = NL_A_NESTED, .optional = false },
- };
-
- static const struct nl_policy linkinfo_policy[] = {
- [IFLA_INFO_KIND] = { .type = NL_A_STRING, .optional = false },
- };
-
- int error;
- bool ret = false;
- struct ofpbuf request, *reply;
- struct ifinfomsg ifinfomsg;
- struct nlattr *getlink_attrs[ARRAY_SIZE(getlink_policy)];
- struct nlattr *linkinfo_attrs[ARRAY_SIZE(linkinfo_policy)];
- struct ofpbuf linkinfo;
- const char *device_kind;
-
- ofpbuf_init(&request, 0);
-
- nl_msg_put_nlmsghdr(&request, gre_descriptors.nl_sock,
- NLMSG_LENGTH(sizeof ifinfomsg), RTM_GETLINK,
- NLM_F_REQUEST);
-
- memset(&ifinfomsg, 0, sizeof ifinfomsg);
- ifinfomsg.ifi_family = AF_UNSPEC;
- ifinfomsg.ifi_index = do_get_ifindex(name);
- nl_msg_put(&request, &ifinfomsg, sizeof ifinfomsg);
-
- error = nl_sock_transact(gre_descriptors.nl_sock, &request, &reply);
- ofpbuf_uninit(&request);
- if (error) {
- VLOG_WARN("couldn't transact netlink socket: %s", strerror(error));
- return false;
- }
-
- if (!nl_policy_parse(reply, NLMSG_HDRLEN + sizeof(struct ifinfomsg),
- getlink_policy, getlink_attrs,
- ARRAY_SIZE(getlink_policy))) {
- VLOG_WARN("received bad rtnl message (getlink policy)");
- goto error;
- }
-
- linkinfo.data = (void *)nl_attr_get(getlink_attrs[IFLA_LINKINFO]);
- linkinfo.size = nl_attr_get_size(getlink_attrs[IFLA_LINKINFO]);
- if (!nl_policy_parse(&linkinfo, 0, linkinfo_policy,
- linkinfo_attrs, ARRAY_SIZE(linkinfo_policy))) {
- VLOG_WARN("received bad rtnl message (linkinfo policy)");
- goto error;
- }
-
- device_kind = nl_attr_get_string(linkinfo_attrs[IFLA_INFO_KIND]);
- ret = !strcmp(device_kind, "gretap");
-
-error:
- ofpbuf_delete(reply);
- return ret;
-#endif
-}
-
-static bool
-check_gre_device_ioctl(const char *name)
-{
- struct ethtool_drvinfo drvinfo;
- int error;
-
- memset(&drvinfo, 0, sizeof drvinfo);
- error = netdev_linux_do_ethtool(name, (struct ethtool_cmd *)&drvinfo,
- ETHTOOL_GDRVINFO, "ETHTOOL_GDRVINFO");
-
- return !error && !strcmp(drvinfo.driver, "ip_gre")
- && !strcmp(drvinfo.bus_info, "gretap");
-}
-
-static int
-setup_gre(const char *name, const struct shash *args, bool create)
-{
- int error;
- struct in_addr in_addr;
- struct shash_node *node;
- struct gre_config config;
-
- memset(&config, 0, sizeof config);
- config.in_csum = true;
- config.out_csum = true;
-
- SHASH_FOR_EACH (node, args) {
- if (!strcmp(node->name, "remote_ip")) {
- if (lookup_ip(node->data, &in_addr)) {
- VLOG_WARN("bad 'remote_ip' for gre device %s ", name);
- } else {
- config.remote_ip = in_addr.s_addr;
- }
- } else if (!strcmp(node->name, "local_ip")) {
- if (lookup_ip(node->data, &in_addr)) {
- VLOG_WARN("bad 'local_ip' for gre device %s ", name);
- } else {
- config.local_ip = in_addr.s_addr;
- }
- } else if (!strcmp(node->name, "key")) {
- config.have_in_key = true;
- config.have_out_key = true;
- config.in_key = htonl(atoi(node->data));
- config.out_key = htonl(atoi(node->data));
- } else if (!strcmp(node->name, "in_key")) {
- config.have_in_key = true;
- config.in_key = htonl(atoi(node->data));
- } else if (!strcmp(node->name, "out_key")) {
- config.have_out_key = true;
- config.out_key = htonl(atoi(node->data));
- } else if (!strcmp(node->name, "csum")) {
- if (!strcmp(node->data, "false")) {
- config.in_csum = false;
- config.out_csum = false;
- }
- } else {
- VLOG_WARN("unknown gre argument '%s'", node->name);
- }
- }
-
- if (!config.remote_ip) {
- VLOG_WARN("gre type requires valid 'remote_ip' argument");
- error = EINVAL;
- goto error;
- }
-
- if (!gre_descriptors.use_ioctl) {
- error = setup_gre_netlink(name, &config, create);
- if (error == EOPNOTSUPP) {
- gre_descriptors.use_ioctl = true;
- }
- }
- if (gre_descriptors.use_ioctl) {
- error = setup_gre_ioctl(name, &config, create);
- }
-
- if (create && error == EEXIST) {
- bool gre_device;
-
- if (gre_descriptors.use_ioctl) {
- gre_device = check_gre_device_ioctl(name);
- } else {
- gre_device = check_gre_device_netlink(name);
- }
-
- if (!gre_device) {
- goto error;
- }
-
- VLOG_WARN("replacing existing gre device %s", name);
- error = destroy_gre(name);
- if (error) {
- goto error;
- }
-
- if (gre_descriptors.use_ioctl) {
- error = setup_gre_ioctl(name, &config, create);
- } else {
- error = setup_gre_netlink(name, &config, create);
- }
- }
-
-error:
- return error;
-}
-