1 /*
2  * hostapd / VLAN netlink api
3  * Copyright (c) 2012, Michael Braun <michael-dev@fami-braun.de>
4  *
5  * This software may be distributed under the terms of the BSD license.
6  * See README for more details.
7  */
8 
9 #include "utils/includes.h"
10 #include <sys/ioctl.h>
11 #include <linux/sockios.h>
12 #include <linux/if_vlan.h>
13 #include <netlink/genl/genl.h>
14 #include <netlink/genl/family.h>
15 #include <netlink/genl/ctrl.h>
16 #include <netlink/route/link.h>
17 #include <netlink/route/link/vlan.h>
18 
19 #include "utils/common.h"
20 #include "utils/eloop.h"
21 #include "hostapd.h"
22 #include "vlan_util.h"
23 
24 /*
25  * Add a vlan interface with name 'vlan_if_name', VLAN ID 'vid' and
26  * tagged interface 'if_name'.
27  *
28  * returns -1 on error
29  * returns 1 if the interface already exists
30  * returns 0 otherwise
31 */
32 int vlan_add(const char *if_name, int vid, const char *vlan_if_name)
33 {
34 	int ret = -1;
35 	struct nl_sock *handle = NULL;
36 	struct nl_cache *cache = NULL;
37 	struct rtnl_link *rlink = NULL;
38 	int if_idx = 0;
39 
40 	wpa_printf(MSG_DEBUG, "VLAN: vlan_add(if_name=%s, vid=%d, "
41 		   "vlan_if_name=%s)", if_name, vid, vlan_if_name);
42 
43 	if ((os_strlen(if_name) + 1) > IFNAMSIZ) {
44 		wpa_printf(MSG_ERROR, "VLAN: Interface name too long: '%s'",
45 			   if_name);
46 		return -1;
47 	}
48 
49 	if ((os_strlen(vlan_if_name) + 1) > IFNAMSIZ) {
50 		wpa_printf(MSG_ERROR, "VLAN: Interface name too long: '%s'",
51 			   vlan_if_name);
52 		return -1;
53 	}
54 
55 	handle = nl_socket_alloc();
56 	if (!handle) {
57 		wpa_printf(MSG_ERROR, "VLAN: failed to open netlink socket");
58 		goto vlan_add_error;
59 	}
60 
61 	if (nl_connect(handle, NETLINK_ROUTE) < 0) {
62 		wpa_printf(MSG_ERROR, "VLAN: failed to connect to netlink");
63 		goto vlan_add_error;
64 	}
65 
66 	if (rtnl_link_alloc_cache(handle, AF_UNSPEC, &cache) < 0) {
67 		cache = NULL;
68 		wpa_printf(MSG_ERROR, "VLAN: failed to alloc cache");
69 		goto vlan_add_error;
70 	}
71 
72 	if (!(if_idx = rtnl_link_name2i(cache, if_name))) {
73 		/* link does not exist */
74 		wpa_printf(MSG_ERROR, "VLAN: interface %s does not exist",
75 			   if_name);
76 		goto vlan_add_error;
77 	}
78 
79 	if ((rlink = rtnl_link_get_by_name(cache, vlan_if_name))) {
80 		/* link does exist */
81 		rtnl_link_put(rlink);
82 		rlink = NULL;
83 		wpa_printf(MSG_ERROR, "VLAN: interface %s already exists",
84 			   vlan_if_name);
85 		ret = 1;
86 		goto vlan_add_error;
87 	}
88 
89 	rlink = rtnl_link_alloc();
90 	if (!rlink) {
91 		wpa_printf(MSG_ERROR, "VLAN: failed to allocate new link");
92 		goto vlan_add_error;
93 	}
94 
95 	if (rtnl_link_set_type(rlink, "vlan") < 0) {
96 		wpa_printf(MSG_ERROR, "VLAN: failed to set link type");
97 		goto vlan_add_error;
98 	}
99 
100 	rtnl_link_set_link(rlink, if_idx);
101 	rtnl_link_set_name(rlink, vlan_if_name);
102 
103 	if (rtnl_link_vlan_set_id(rlink, vid) < 0) {
104 		wpa_printf(MSG_ERROR, "VLAN: failed to set link vlan id");
105 		goto vlan_add_error;
106 	}
107 
108 	if (rtnl_link_add(handle, rlink, NLM_F_CREATE) < 0) {
109 		wpa_printf(MSG_ERROR, "VLAN: failed to create link %s for "
110 			   "vlan %d on %s (%d)",
111 			   vlan_if_name, vid, if_name, if_idx);
112 		goto vlan_add_error;
113 	}
114 
115 	ret = 0;
116 
117 vlan_add_error:
118 	if (rlink)
119 		rtnl_link_put(rlink);
120 	if (cache)
121 		nl_cache_free(cache);
122 	if (handle)
123 		nl_socket_free(handle);
124 	return ret;
125 }
126 
127 
128 int vlan_rem(const char *if_name)
129 {
130 	int ret = -1;
131 	struct nl_sock *handle = NULL;
132 	struct nl_cache *cache = NULL;
133 	struct rtnl_link *rlink = NULL;
134 
135 	wpa_printf(MSG_DEBUG, "VLAN: vlan_rem(if_name=%s)", if_name);
136 
137 	handle = nl_socket_alloc();
138 	if (!handle) {
139 		wpa_printf(MSG_ERROR, "VLAN: failed to open netlink socket");
140 		goto vlan_rem_error;
141 	}
142 
143 	if (nl_connect(handle, NETLINK_ROUTE) < 0) {
144 		wpa_printf(MSG_ERROR, "VLAN: failed to connect to netlink");
145 		goto vlan_rem_error;
146 	}
147 
148 	if (rtnl_link_alloc_cache(handle, AF_UNSPEC, &cache) < 0) {
149 		cache = NULL;
150 		wpa_printf(MSG_ERROR, "VLAN: failed to alloc cache");
151 		goto vlan_rem_error;
152 	}
153 
154 	if (!(rlink = rtnl_link_get_by_name(cache, if_name))) {
155 		/* link does not exist */
156 		wpa_printf(MSG_ERROR, "VLAN: interface %s does not exists",
157 			   if_name);
158 		goto vlan_rem_error;
159 	}
160 
161 	if (rtnl_link_delete(handle, rlink) < 0) {
162 		wpa_printf(MSG_ERROR, "VLAN: failed to remove link %s",
163 			   if_name);
164 		goto vlan_rem_error;
165 	}
166 
167 	ret = 0;
168 
169 vlan_rem_error:
170 	if (rlink)
171 		rtnl_link_put(rlink);
172 	if (cache)
173 		nl_cache_free(cache);
174 	if (handle)
175 		nl_socket_free(handle);
176 	return ret;
177 }
178