161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt/*
261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt * hostapd / VLAN netlink api
361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt * Copyright (c) 2012, Michael Braun <michael-dev@fami-braun.de>
461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt *
561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt * This software may be distributed under the terms of the BSD license.
661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt * See README for more details.
761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt */
861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt#include "utils/includes.h"
1061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt#include <sys/ioctl.h>
1161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt#include <linux/sockios.h>
1261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt#include <linux/if_vlan.h>
1361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt#include <netlink/genl/genl.h>
1461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt#include <netlink/genl/family.h>
1561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt#include <netlink/genl/ctrl.h>
1661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt#include <netlink/route/link.h>
1761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt#include <netlink/route/link/vlan.h>
1861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
1961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt#include "utils/common.h"
2061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt#include "utils/eloop.h"
2161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt#include "hostapd.h"
2261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt#include "vlan_util.h"
2361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
2461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt/*
2561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt * Add a vlan interface with name 'vlan_if_name', VLAN ID 'vid' and
2661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt * tagged interface 'if_name'.
2761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt *
2861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt * returns -1 on error
2961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt * returns 1 if the interface already exists
3061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt * returns 0 otherwise
3161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt*/
3261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidtint vlan_add(const char *if_name, int vid, const char *vlan_if_name)
3361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt{
3461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	int ret = -1;
3561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	struct nl_sock *handle = NULL;
3661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	struct nl_cache *cache = NULL;
3761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	struct rtnl_link *rlink = NULL;
3861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	int if_idx = 0;
3961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
4061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	wpa_printf(MSG_DEBUG, "VLAN: vlan_add(if_name=%s, vid=%d, "
4161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		   "vlan_if_name=%s)", if_name, vid, vlan_if_name);
4261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
4361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	if ((os_strlen(if_name) + 1) > IFNAMSIZ) {
4461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpa_printf(MSG_ERROR, "VLAN: Interface name too long: '%s'",
4561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			   if_name);
4661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		return -1;
4761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	}
4861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
4961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	if ((os_strlen(vlan_if_name) + 1) > IFNAMSIZ) {
5061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpa_printf(MSG_ERROR, "VLAN: Interface name too long: '%s'",
5161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			   vlan_if_name);
5261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		return -1;
5361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	}
5461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
5561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	handle = nl_socket_alloc();
5661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	if (!handle) {
5761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpa_printf(MSG_ERROR, "VLAN: failed to open netlink socket");
5861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		goto vlan_add_error;
5961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	}
6061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
6161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	if (nl_connect(handle, NETLINK_ROUTE) < 0) {
6261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpa_printf(MSG_ERROR, "VLAN: failed to connect to netlink");
6361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		goto vlan_add_error;
6461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	}
6561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
6661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	if (rtnl_link_alloc_cache(handle, AF_UNSPEC, &cache) < 0) {
6761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		cache = NULL;
6861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpa_printf(MSG_ERROR, "VLAN: failed to alloc cache");
6961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		goto vlan_add_error;
7061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	}
7161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
7261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	if (!(if_idx = rtnl_link_name2i(cache, if_name))) {
7361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		/* link does not exist */
7461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpa_printf(MSG_ERROR, "VLAN: interface %s does not exist",
7561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			   if_name);
7661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		goto vlan_add_error;
7761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	}
7861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
7961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	if ((rlink = rtnl_link_get_by_name(cache, vlan_if_name))) {
8061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		/* link does exist */
8161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		rtnl_link_put(rlink);
8261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		rlink = NULL;
8361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpa_printf(MSG_ERROR, "VLAN: interface %s already exists",
8461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			   vlan_if_name);
8561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		ret = 1;
8661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		goto vlan_add_error;
8761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	}
8861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
8961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	rlink = rtnl_link_alloc();
9061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	if (!rlink) {
9161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpa_printf(MSG_ERROR, "VLAN: failed to allocate new link");
9261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		goto vlan_add_error;
9361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	}
9461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
9561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	if (rtnl_link_set_type(rlink, "vlan") < 0) {
9661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpa_printf(MSG_ERROR, "VLAN: failed to set link type");
9761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		goto vlan_add_error;
9861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	}
9961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
10061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	rtnl_link_set_link(rlink, if_idx);
10161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	rtnl_link_set_name(rlink, vlan_if_name);
10261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
10361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	if (rtnl_link_vlan_set_id(rlink, vid) < 0) {
10461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpa_printf(MSG_ERROR, "VLAN: failed to set link vlan id");
10561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		goto vlan_add_error;
10661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	}
10761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
10861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	if (rtnl_link_add(handle, rlink, NLM_F_CREATE) < 0) {
10961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpa_printf(MSG_ERROR, "VLAN: failed to create link %s for "
11061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			   "vlan %d on %s (%d)",
11161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			   vlan_if_name, vid, if_name, if_idx);
11261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		goto vlan_add_error;
11361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	}
11461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
11561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	ret = 0;
11661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
11761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidtvlan_add_error:
11861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	if (rlink)
11961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		rtnl_link_put(rlink);
12061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	if (cache)
12161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		nl_cache_free(cache);
12261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	if (handle)
12361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		nl_socket_free(handle);
12461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	return ret;
12561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt}
12661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
12761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
12861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidtint vlan_rem(const char *if_name)
12961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt{
13061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	int ret = -1;
13161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	struct nl_sock *handle = NULL;
13261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	struct nl_cache *cache = NULL;
13361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	struct rtnl_link *rlink = NULL;
13461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
13561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	wpa_printf(MSG_DEBUG, "VLAN: vlan_rem(if_name=%s)", if_name);
13661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
13761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	handle = nl_socket_alloc();
13861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	if (!handle) {
13961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpa_printf(MSG_ERROR, "VLAN: failed to open netlink socket");
14061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		goto vlan_rem_error;
14161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	}
14261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
14361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	if (nl_connect(handle, NETLINK_ROUTE) < 0) {
14461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpa_printf(MSG_ERROR, "VLAN: failed to connect to netlink");
14561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		goto vlan_rem_error;
14661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	}
14761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
14861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	if (rtnl_link_alloc_cache(handle, AF_UNSPEC, &cache) < 0) {
14961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		cache = NULL;
15061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpa_printf(MSG_ERROR, "VLAN: failed to alloc cache");
15161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		goto vlan_rem_error;
15261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	}
15361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
15461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	if (!(rlink = rtnl_link_get_by_name(cache, if_name))) {
15561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		/* link does not exist */
15661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpa_printf(MSG_ERROR, "VLAN: interface %s does not exists",
15761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			   if_name);
15861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		goto vlan_rem_error;
15961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	}
16061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
16161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	if (rtnl_link_delete(handle, rlink) < 0) {
16261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpa_printf(MSG_ERROR, "VLAN: failed to remove link %s",
16361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			   if_name);
16461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		goto vlan_rem_error;
16561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	}
16661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
16761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	ret = 0;
16861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
16961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidtvlan_rem_error:
17061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	if (rlink)
17161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		rtnl_link_put(rlink);
17261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	if (cache)
17361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		nl_cache_free(cache);
17461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	if (handle)
17561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		nl_socket_free(handle);
17661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	return ret;
17761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt}
178