2 * This is the new netlink-based wireless configuration interface.
4 * Copyright 2006, 2007 Johannes Berg <johannes@sipsolutions.net>
8 #include <linux/module.h>
10 #include <linux/mutex.h>
11 #include <linux/list.h>
12 #include <linux/if_ether.h>
13 #include <linux/ieee80211.h>
14 #include <linux/nl80211.h>
15 #include <linux/rtnetlink.h>
16 #include <linux/netlink.h>
17 #include <net/genetlink.h>
18 #include <net/cfg80211.h>
22 /* the netlink family */
23 static struct genl_family nl80211_fam
= {
24 .id
= GENL_ID_GENERATE
, /* don't bother with a hardcoded ID */
25 .name
= "nl80211", /* have users key off the name instead */
26 .hdrsize
= 0, /* no private header */
27 .version
= 1, /* no particular meaning now */
28 .maxattr
= NL80211_ATTR_MAX
,
31 /* internal helper: get drv and dev */
32 static int get_drv_dev_by_info_ifindex(struct genl_info
*info
,
33 struct cfg80211_registered_device
**drv
,
34 struct net_device
**dev
)
38 if (!info
->attrs
[NL80211_ATTR_IFINDEX
])
41 ifindex
= nla_get_u32(info
->attrs
[NL80211_ATTR_IFINDEX
]);
42 *dev
= dev_get_by_index(&init_net
, ifindex
);
46 *drv
= cfg80211_get_dev_from_ifindex(ifindex
);
55 /* policy for the attributes */
56 static struct nla_policy nl80211_policy
[NL80211_ATTR_MAX
+1] __read_mostly
= {
57 [NL80211_ATTR_WIPHY
] = { .type
= NLA_U32
},
58 [NL80211_ATTR_WIPHY_NAME
] = { .type
= NLA_NUL_STRING
,
59 .len
= BUS_ID_SIZE
-1 },
61 [NL80211_ATTR_IFTYPE
] = { .type
= NLA_U32
},
62 [NL80211_ATTR_IFINDEX
] = { .type
= NLA_U32
},
63 [NL80211_ATTR_IFNAME
] = { .type
= NLA_NUL_STRING
, .len
= IFNAMSIZ
-1 },
66 /* message building helper */
67 static inline void *nl80211hdr_put(struct sk_buff
*skb
, u32 pid
, u32 seq
,
70 /* since there is no private header just add the generic one */
71 return genlmsg_put(skb
, pid
, seq
, &nl80211_fam
, flags
, cmd
);
74 /* netlink command implementations */
76 static int nl80211_send_wiphy(struct sk_buff
*msg
, u32 pid
, u32 seq
, int flags
,
77 struct cfg80211_registered_device
*dev
)
81 hdr
= nl80211hdr_put(msg
, pid
, seq
, flags
, NL80211_CMD_NEW_WIPHY
);
85 NLA_PUT_U32(msg
, NL80211_ATTR_WIPHY
, dev
->idx
);
86 NLA_PUT_STRING(msg
, NL80211_ATTR_WIPHY_NAME
, wiphy_name(&dev
->wiphy
));
87 return genlmsg_end(msg
, hdr
);
90 return genlmsg_cancel(msg
, hdr
);
93 static int nl80211_dump_wiphy(struct sk_buff
*skb
, struct netlink_callback
*cb
)
96 int start
= cb
->args
[0];
97 struct cfg80211_registered_device
*dev
;
99 mutex_lock(&cfg80211_drv_mutex
);
100 list_for_each_entry(dev
, &cfg80211_drv_list
, list
) {
103 if (nl80211_send_wiphy(skb
, NETLINK_CB(cb
->skb
).pid
,
104 cb
->nlh
->nlmsg_seq
, NLM_F_MULTI
,
108 mutex_unlock(&cfg80211_drv_mutex
);
115 static int nl80211_get_wiphy(struct sk_buff
*skb
, struct genl_info
*info
)
118 struct cfg80211_registered_device
*dev
;
120 dev
= cfg80211_get_dev_from_info(info
);
124 msg
= nlmsg_new(NLMSG_GOODSIZE
, GFP_KERNEL
);
128 if (nl80211_send_wiphy(msg
, info
->snd_pid
, info
->snd_seq
, 0, dev
) < 0)
131 cfg80211_put_dev(dev
);
133 return genlmsg_unicast(msg
, info
->snd_pid
);
138 cfg80211_put_dev(dev
);
142 static int nl80211_set_wiphy(struct sk_buff
*skb
, struct genl_info
*info
)
144 struct cfg80211_registered_device
*rdev
;
147 if (!info
->attrs
[NL80211_ATTR_WIPHY_NAME
])
150 rdev
= cfg80211_get_dev_from_info(info
);
152 return PTR_ERR(rdev
);
154 result
= cfg80211_dev_rename(rdev
, nla_data(info
->attrs
[NL80211_ATTR_WIPHY_NAME
]));
156 cfg80211_put_dev(rdev
);
161 static int nl80211_send_iface(struct sk_buff
*msg
, u32 pid
, u32 seq
, int flags
,
162 struct net_device
*dev
)
166 hdr
= nl80211hdr_put(msg
, pid
, seq
, flags
, NL80211_CMD_NEW_INTERFACE
);
170 NLA_PUT_U32(msg
, NL80211_ATTR_IFINDEX
, dev
->ifindex
);
171 NLA_PUT_STRING(msg
, NL80211_ATTR_IFNAME
, dev
->name
);
172 /* TODO: interface type */
173 return genlmsg_end(msg
, hdr
);
176 return genlmsg_cancel(msg
, hdr
);
179 static int nl80211_dump_interface(struct sk_buff
*skb
, struct netlink_callback
*cb
)
183 int wp_start
= cb
->args
[0];
184 int if_start
= cb
->args
[1];
185 struct cfg80211_registered_device
*dev
;
186 struct wireless_dev
*wdev
;
188 mutex_lock(&cfg80211_drv_mutex
);
189 list_for_each_entry(dev
, &cfg80211_drv_list
, list
) {
190 if (++wp_idx
< wp_start
)
194 mutex_lock(&dev
->devlist_mtx
);
195 list_for_each_entry(wdev
, &dev
->netdev_list
, list
) {
196 if (++if_idx
< if_start
)
198 if (nl80211_send_iface(skb
, NETLINK_CB(cb
->skb
).pid
,
199 cb
->nlh
->nlmsg_seq
, NLM_F_MULTI
,
203 mutex_unlock(&dev
->devlist_mtx
);
205 mutex_unlock(&cfg80211_drv_mutex
);
207 cb
->args
[0] = wp_idx
;
208 cb
->args
[1] = if_idx
;
213 static int nl80211_get_interface(struct sk_buff
*skb
, struct genl_info
*info
)
216 struct cfg80211_registered_device
*dev
;
217 struct net_device
*netdev
;
220 err
= get_drv_dev_by_info_ifindex(info
, &dev
, &netdev
);
224 msg
= nlmsg_new(NLMSG_GOODSIZE
, GFP_KERNEL
);
228 if (nl80211_send_iface(msg
, info
->snd_pid
, info
->snd_seq
, 0, netdev
) < 0)
232 cfg80211_put_dev(dev
);
234 return genlmsg_unicast(msg
, info
->snd_pid
);
240 cfg80211_put_dev(dev
);
244 static int nl80211_set_interface(struct sk_buff
*skb
, struct genl_info
*info
)
246 struct cfg80211_registered_device
*drv
;
248 enum nl80211_iftype type
;
249 struct net_device
*dev
;
251 if (info
->attrs
[NL80211_ATTR_IFTYPE
]) {
252 type
= nla_get_u32(info
->attrs
[NL80211_ATTR_IFTYPE
]);
253 if (type
> NL80211_IFTYPE_MAX
)
258 err
= get_drv_dev_by_info_ifindex(info
, &drv
, &dev
);
261 ifindex
= dev
->ifindex
;
264 if (!drv
->ops
->change_virtual_intf
) {
270 err
= drv
->ops
->change_virtual_intf(&drv
->wiphy
, ifindex
, type
);
274 cfg80211_put_dev(drv
);
278 static int nl80211_new_interface(struct sk_buff
*skb
, struct genl_info
*info
)
280 struct cfg80211_registered_device
*drv
;
282 enum nl80211_iftype type
= NL80211_IFTYPE_UNSPECIFIED
;
284 if (!info
->attrs
[NL80211_ATTR_IFNAME
])
287 if (info
->attrs
[NL80211_ATTR_IFTYPE
]) {
288 type
= nla_get_u32(info
->attrs
[NL80211_ATTR_IFTYPE
]);
289 if (type
> NL80211_IFTYPE_MAX
)
293 drv
= cfg80211_get_dev_from_info(info
);
297 if (!drv
->ops
->add_virtual_intf
) {
303 err
= drv
->ops
->add_virtual_intf(&drv
->wiphy
,
304 nla_data(info
->attrs
[NL80211_ATTR_IFNAME
]), type
);
308 cfg80211_put_dev(drv
);
312 static int nl80211_del_interface(struct sk_buff
*skb
, struct genl_info
*info
)
314 struct cfg80211_registered_device
*drv
;
316 struct net_device
*dev
;
318 err
= get_drv_dev_by_info_ifindex(info
, &drv
, &dev
);
321 ifindex
= dev
->ifindex
;
324 if (!drv
->ops
->del_virtual_intf
) {
330 err
= drv
->ops
->del_virtual_intf(&drv
->wiphy
, ifindex
);
334 cfg80211_put_dev(drv
);
338 static struct genl_ops nl80211_ops
[] = {
340 .cmd
= NL80211_CMD_GET_WIPHY
,
341 .doit
= nl80211_get_wiphy
,
342 .dumpit
= nl80211_dump_wiphy
,
343 .policy
= nl80211_policy
,
344 /* can be retrieved by unprivileged users */
347 .cmd
= NL80211_CMD_SET_WIPHY
,
348 .doit
= nl80211_set_wiphy
,
349 .policy
= nl80211_policy
,
350 .flags
= GENL_ADMIN_PERM
,
353 .cmd
= NL80211_CMD_GET_INTERFACE
,
354 .doit
= nl80211_get_interface
,
355 .dumpit
= nl80211_dump_interface
,
356 .policy
= nl80211_policy
,
357 /* can be retrieved by unprivileged users */
360 .cmd
= NL80211_CMD_SET_INTERFACE
,
361 .doit
= nl80211_set_interface
,
362 .policy
= nl80211_policy
,
363 .flags
= GENL_ADMIN_PERM
,
366 .cmd
= NL80211_CMD_NEW_INTERFACE
,
367 .doit
= nl80211_new_interface
,
368 .policy
= nl80211_policy
,
369 .flags
= GENL_ADMIN_PERM
,
372 .cmd
= NL80211_CMD_DEL_INTERFACE
,
373 .doit
= nl80211_del_interface
,
374 .policy
= nl80211_policy
,
375 .flags
= GENL_ADMIN_PERM
,
379 /* multicast groups */
380 static struct genl_multicast_group nl80211_config_mcgrp
= {
384 /* notification functions */
386 void nl80211_notify_dev_rename(struct cfg80211_registered_device
*rdev
)
390 msg
= nlmsg_new(NLMSG_GOODSIZE
, GFP_KERNEL
);
394 if (nl80211_send_wiphy(msg
, 0, 0, 0, rdev
) < 0) {
399 genlmsg_multicast(msg
, 0, nl80211_config_mcgrp
.id
, GFP_KERNEL
);
402 /* initialisation/exit functions */
404 int nl80211_init(void)
408 err
= genl_register_family(&nl80211_fam
);
412 for (i
= 0; i
< ARRAY_SIZE(nl80211_ops
); i
++) {
413 err
= genl_register_ops(&nl80211_fam
, &nl80211_ops
[i
]);
418 err
= genl_register_mc_group(&nl80211_fam
, &nl80211_config_mcgrp
);
424 genl_unregister_family(&nl80211_fam
);
428 void nl80211_exit(void)
430 genl_unregister_family(&nl80211_fam
);