Skip to content

Commit e5497d7

Browse files
jmberg-intellinvjw
authored andcommitted
cfg80211/nl80211: support GTK rekey offload
In certain circumstances, like WoWLAN scenarios, devices may implement (partial) GTK rekeying on the device to avoid waking up the host for it. In order to successfully go through GTK rekeying, the KEK, KCK and the replay counter are required. Add API to let the supplicant hand the parameters to the driver which may store it for future GTK rekey operations. Note that, of course, if GTK rekeying is done by the device, the EAP frame must not be passed up to userspace, instead a rekey event needs to be sent to let userspace update its replay counter. Signed-off-by: Johannes Berg <johannes.berg@intel.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
1 parent 830af02 commit e5497d7

File tree

5 files changed

+193
-0
lines changed

5 files changed

+193
-0
lines changed

include/linux/nl80211.h

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -483,6 +483,14 @@
483483
* more background information, see
484484
* http://wireless.kernel.org/en/users/Documentation/WoWLAN.
485485
*
486+
* @NL80211_CMD_SET_REKEY_OFFLOAD: This command is used give the driver
487+
* the necessary information for supporting GTK rekey offload. This
488+
* feature is typically used during WoWLAN. The configuration data
489+
* is contained in %NL80211_ATTR_REKEY_DATA (which is nested and
490+
* contains the data in sub-attributes). After rekeying happened,
491+
* this command may also be sent by the driver as an MLME event to
492+
* inform userspace of the new replay counter.
493+
*
486494
* @NL80211_CMD_MAX: highest used command number
487495
* @__NL80211_CMD_AFTER_LAST: internal use
488496
*/
@@ -605,6 +613,8 @@ enum nl80211_commands {
605613
NL80211_CMD_SCHED_SCAN_RESULTS,
606614
NL80211_CMD_SCHED_SCAN_STOPPED,
607615

616+
NL80211_CMD_SET_REKEY_OFFLOAD,
617+
608618
/* add new commands above here */
609619

610620
/* used to define NL80211_CMD_MAX below */
@@ -996,6 +1006,9 @@ enum nl80211_commands {
9961006
* are managed in software: interfaces of these types aren't subject to
9971007
* any restrictions in their number or combinations.
9981008
*
1009+
* @%NL80211_ATTR_REKEY_DATA: nested attribute containing the information
1010+
* necessary for GTK rekeying in the device, see &enum nl80211_rekey_data.
1011+
*
9991012
* @NL80211_ATTR_MAX: highest attribute number currently defined
10001013
* @__NL80211_ATTR_AFTER_LAST: internal use
10011014
*/
@@ -1194,6 +1207,8 @@ enum nl80211_attrs {
11941207
NL80211_ATTR_INTERFACE_COMBINATIONS,
11951208
NL80211_ATTR_SOFTWARE_IFTYPES,
11961209

1210+
NL80211_ATTR_REKEY_DATA,
1211+
11971212
/* add attributes here, update the policy in nl80211.c */
11981213

11991214
__NL80211_ATTR_AFTER_LAST,
@@ -2361,4 +2376,28 @@ enum nl80211_plink_state {
23612376
MAX_NL80211_PLINK_STATES = NUM_NL80211_PLINK_STATES - 1
23622377
};
23632378

2379+
#define NL80211_KCK_LEN 16
2380+
#define NL80211_KEK_LEN 16
2381+
#define NL80211_REPLAY_CTR_LEN 8
2382+
2383+
/**
2384+
* enum nl80211_rekey_data - attributes for GTK rekey offload
2385+
* @__NL80211_REKEY_DATA_INVALID: invalid number for nested attributes
2386+
* @NL80211_REKEY_DATA_KEK: key encryption key (binary)
2387+
* @NL80211_REKEY_DATA_KCK: key confirmation key (binary)
2388+
* @NL80211_REKEY_DATA_REPLAY_CTR: replay counter (binary)
2389+
* @NUM_NL80211_REKEY_DATA: number of rekey attributes (internal)
2390+
* @MAX_NL80211_REKEY_DATA: highest rekey attribute (internal)
2391+
*/
2392+
enum nl80211_rekey_data {
2393+
__NL80211_REKEY_DATA_INVALID,
2394+
NL80211_REKEY_DATA_KEK,
2395+
NL80211_REKEY_DATA_KCK,
2396+
NL80211_REKEY_DATA_REPLAY_CTR,
2397+
2398+
/* keep last */
2399+
NUM_NL80211_REKEY_DATA,
2400+
MAX_NL80211_REKEY_DATA = NUM_NL80211_REKEY_DATA - 1
2401+
};
2402+
23642403
#endif /* __LINUX_NL80211_H */

include/net/cfg80211.h

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1153,6 +1153,18 @@ struct cfg80211_wowlan {
11531153
int n_patterns;
11541154
};
11551155

1156+
/**
1157+
* struct cfg80211_gtk_rekey_data - rekey data
1158+
* @kek: key encryption key
1159+
* @kck: key confirmation key
1160+
* @replay_ctr: replay counter
1161+
*/
1162+
struct cfg80211_gtk_rekey_data {
1163+
u8 kek[NL80211_KEK_LEN];
1164+
u8 kck[NL80211_KCK_LEN];
1165+
u8 replay_ctr[NL80211_REPLAY_CTR_LEN];
1166+
};
1167+
11561168
/**
11571169
* struct cfg80211_ops - backend description for wireless configuration
11581170
*
@@ -1197,6 +1209,8 @@ struct cfg80211_wowlan {
11971209
*
11981210
* @set_default_mgmt_key: set the default management frame key on an interface
11991211
*
1212+
* @set_rekey_data: give the data necessary for GTK rekeying to the driver
1213+
*
12001214
* @add_beacon: Add a beacon with given parameters, @head, @interval
12011215
* and @dtim_period will be valid, @tail is optional.
12021216
* @set_beacon: Change the beacon parameters for an access point mode
@@ -1499,6 +1513,9 @@ struct cfg80211_ops {
14991513
struct net_device *dev,
15001514
struct cfg80211_sched_scan_request *request);
15011515
int (*sched_scan_stop)(struct wiphy *wiphy, struct net_device *dev);
1516+
1517+
int (*set_rekey_data)(struct wiphy *wiphy, struct net_device *dev,
1518+
struct cfg80211_gtk_rekey_data *data);
15021519
};
15031520

15041521
/*
@@ -3033,6 +3050,15 @@ void cfg80211_cqm_rssi_notify(struct net_device *dev,
30333050
void cfg80211_cqm_pktloss_notify(struct net_device *dev,
30343051
const u8 *peer, u32 num_packets, gfp_t gfp);
30353052

3053+
/**
3054+
* cfg80211_gtk_rekey_notify - notify userspace about driver rekeying
3055+
* @dev: network device
3056+
* @bssid: BSSID of AP (to avoid races)
3057+
* @replay_ctr: new replay counter
3058+
*/
3059+
void cfg80211_gtk_rekey_notify(struct net_device *dev, const u8 *bssid,
3060+
const u8 *replay_ctr, gfp_t gfp);
3061+
30363062
/* Logging, debugging and troubleshooting/diagnostic helpers. */
30373063

30383064
/* wiphy_printk helpers, similar to dev_printk */

net/wireless/mlme.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1084,3 +1084,14 @@ void cfg80211_cqm_pktloss_notify(struct net_device *dev,
10841084
nl80211_send_cqm_pktloss_notify(rdev, dev, peer, num_packets, gfp);
10851085
}
10861086
EXPORT_SYMBOL(cfg80211_cqm_pktloss_notify);
1087+
1088+
void cfg80211_gtk_rekey_notify(struct net_device *dev, const u8 *bssid,
1089+
const u8 *replay_ctr, gfp_t gfp)
1090+
{
1091+
struct wireless_dev *wdev = dev->ieee80211_ptr;
1092+
struct wiphy *wiphy = wdev->wiphy;
1093+
struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
1094+
1095+
nl80211_gtk_rekey_notify(rdev, dev, bssid, replay_ctr, gfp);
1096+
}
1097+
EXPORT_SYMBOL(cfg80211_gtk_rekey_notify);

net/wireless/nl80211.c

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,7 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
176176
[NL80211_ATTR_WOWLAN_TRIGGERS] = { .type = NLA_NESTED },
177177
[NL80211_ATTR_STA_PLINK_STATE] = { .type = NLA_U8 },
178178
[NL80211_ATTR_SCHED_SCAN_INTERVAL] = { .type = NLA_U32 },
179+
[NL80211_ATTR_REKEY_DATA] = { .type = NLA_NESTED },
179180
};
180181

181182
/* policy for the key attributes */
@@ -206,6 +207,14 @@ nl80211_wowlan_policy[NUM_NL80211_WOWLAN_TRIG] = {
206207
[NL80211_WOWLAN_TRIG_PKT_PATTERN] = { .type = NLA_NESTED },
207208
};
208209

210+
/* policy for GTK rekey offload attributes */
211+
static const struct nla_policy
212+
nl80211_rekey_policy[NUM_NL80211_REKEY_DATA] = {
213+
[NL80211_REKEY_DATA_KEK] = { .len = NL80211_KEK_LEN },
214+
[NL80211_REKEY_DATA_KCK] = { .len = NL80211_KCK_LEN },
215+
[NL80211_REKEY_DATA_REPLAY_CTR] = { .len = NL80211_REPLAY_CTR_LEN },
216+
};
217+
209218
/* ifidx get helper */
210219
static int nl80211_get_ifidx(struct netlink_callback *cb)
211220
{
@@ -5408,6 +5417,57 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info)
54085417
return err;
54095418
}
54105419

5420+
static int nl80211_set_rekey_data(struct sk_buff *skb, struct genl_info *info)
5421+
{
5422+
struct cfg80211_registered_device *rdev = info->user_ptr[0];
5423+
struct net_device *dev = info->user_ptr[1];
5424+
struct wireless_dev *wdev = dev->ieee80211_ptr;
5425+
struct nlattr *tb[NUM_NL80211_REKEY_DATA];
5426+
struct cfg80211_gtk_rekey_data rekey_data;
5427+
int err;
5428+
5429+
if (!info->attrs[NL80211_ATTR_REKEY_DATA])
5430+
return -EINVAL;
5431+
5432+
err = nla_parse(tb, MAX_NL80211_REKEY_DATA,
5433+
nla_data(info->attrs[NL80211_ATTR_REKEY_DATA]),
5434+
nla_len(info->attrs[NL80211_ATTR_REKEY_DATA]),
5435+
nl80211_rekey_policy);
5436+
if (err)
5437+
return err;
5438+
5439+
if (nla_len(tb[NL80211_REKEY_DATA_REPLAY_CTR]) != NL80211_REPLAY_CTR_LEN)
5440+
return -ERANGE;
5441+
if (nla_len(tb[NL80211_REKEY_DATA_KEK]) != NL80211_KEK_LEN)
5442+
return -ERANGE;
5443+
if (nla_len(tb[NL80211_REKEY_DATA_KCK]) != NL80211_KCK_LEN)
5444+
return -ERANGE;
5445+
5446+
memcpy(rekey_data.kek, nla_data(tb[NL80211_REKEY_DATA_KEK]),
5447+
NL80211_KEK_LEN);
5448+
memcpy(rekey_data.kck, nla_data(tb[NL80211_REKEY_DATA_KCK]),
5449+
NL80211_KCK_LEN);
5450+
memcpy(rekey_data.replay_ctr,
5451+
nla_data(tb[NL80211_REKEY_DATA_REPLAY_CTR]),
5452+
NL80211_REPLAY_CTR_LEN);
5453+
5454+
wdev_lock(wdev);
5455+
if (!wdev->current_bss) {
5456+
err = -ENOTCONN;
5457+
goto out;
5458+
}
5459+
5460+
if (!rdev->ops->set_rekey_data) {
5461+
err = -EOPNOTSUPP;
5462+
goto out;
5463+
}
5464+
5465+
err = rdev->ops->set_rekey_data(&rdev->wiphy, dev, &rekey_data);
5466+
out:
5467+
wdev_unlock(wdev);
5468+
return err;
5469+
}
5470+
54115471
#define NL80211_FLAG_NEED_WIPHY 0x01
54125472
#define NL80211_FLAG_NEED_NETDEV 0x02
54135473
#define NL80211_FLAG_NEED_RTNL 0x04
@@ -5939,6 +5999,14 @@ static struct genl_ops nl80211_ops[] = {
59395999
.internal_flags = NL80211_FLAG_NEED_WIPHY |
59406000
NL80211_FLAG_NEED_RTNL,
59416001
},
6002+
{
6003+
.cmd = NL80211_CMD_SET_REKEY_OFFLOAD,
6004+
.doit = nl80211_set_rekey_data,
6005+
.policy = nl80211_policy,
6006+
.flags = GENL_ADMIN_PERM,
6007+
.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
6008+
NL80211_FLAG_NEED_RTNL,
6009+
},
59426010
};
59436011

59446012
static struct genl_multicast_group nl80211_mlme_mcgrp = {
@@ -6883,6 +6951,51 @@ nl80211_send_cqm_rssi_notify(struct cfg80211_registered_device *rdev,
68836951
nlmsg_free(msg);
68846952
}
68856953

6954+
void nl80211_gtk_rekey_notify(struct cfg80211_registered_device *rdev,
6955+
struct net_device *netdev, const u8 *bssid,
6956+
const u8 *replay_ctr, gfp_t gfp)
6957+
{
6958+
struct sk_buff *msg;
6959+
struct nlattr *rekey_attr;
6960+
void *hdr;
6961+
6962+
msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
6963+
if (!msg)
6964+
return;
6965+
6966+
hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_SET_REKEY_OFFLOAD);
6967+
if (!hdr) {
6968+
nlmsg_free(msg);
6969+
return;
6970+
}
6971+
6972+
NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
6973+
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
6974+
NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid);
6975+
6976+
rekey_attr = nla_nest_start(msg, NL80211_ATTR_REKEY_DATA);
6977+
if (!rekey_attr)
6978+
goto nla_put_failure;
6979+
6980+
NLA_PUT(msg, NL80211_REKEY_DATA_REPLAY_CTR,
6981+
NL80211_REPLAY_CTR_LEN, replay_ctr);
6982+
6983+
nla_nest_end(msg, rekey_attr);
6984+
6985+
if (genlmsg_end(msg, hdr) < 0) {
6986+
nlmsg_free(msg);
6987+
return;
6988+
}
6989+
6990+
genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
6991+
nl80211_mlme_mcgrp.id, gfp);
6992+
return;
6993+
6994+
nla_put_failure:
6995+
genlmsg_cancel(msg, hdr);
6996+
nlmsg_free(msg);
6997+
}
6998+
68866999
void
68877000
nl80211_send_cqm_pktloss_notify(struct cfg80211_registered_device *rdev,
68887001
struct net_device *netdev, const u8 *peer,

net/wireless/nl80211.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,4 +109,8 @@ nl80211_send_cqm_pktloss_notify(struct cfg80211_registered_device *rdev,
109109
struct net_device *netdev, const u8 *peer,
110110
u32 num_packets, gfp_t gfp);
111111

112+
void nl80211_gtk_rekey_notify(struct cfg80211_registered_device *rdev,
113+
struct net_device *netdev, const u8 *bssid,
114+
const u8 *replay_ctr, gfp_t gfp);
115+
112116
#endif /* __NET_WIRELESS_NL80211_H */

0 commit comments

Comments
 (0)