1341618Scy/*
2341618Scy * Driver interaction with Linux MACsec kernel module
3341618Scy * Copyright (c) 2016, Sabrina Dubroca <sd@queasysnail.net> and Red Hat, Inc.
4351611Scy * Copyright (c) 2019, The Linux Foundation
5341618Scy *
6341618Scy * This software may be distributed under the terms of the BSD license.
7341618Scy * See README for more details.
8341618Scy */
9341618Scy
10341618Scy#include "includes.h"
11341618Scy#include <sys/ioctl.h>
12341618Scy#include <net/if.h>
13341618Scy#include <netpacket/packet.h>
14341618Scy#include <net/if_arp.h>
15341618Scy#include <net/if.h>
16341618Scy#include <netlink/netlink.h>
17341618Scy#include <netlink/genl/genl.h>
18341618Scy#include <netlink/genl/ctrl.h>
19341618Scy#include <netlink/route/link.h>
20341618Scy#include <netlink/route/link/macsec.h>
21341618Scy#include <linux/if_macsec.h>
22341618Scy#include <inttypes.h>
23341618Scy
24341618Scy#include "utils/common.h"
25341618Scy#include "utils/eloop.h"
26351611Scy#include "common/eapol_common.h"
27341618Scy#include "pae/ieee802_1x_kay.h"
28341618Scy#include "driver.h"
29341618Scy#include "driver_wired_common.h"
30341618Scy
31341618Scy#define DRV_PREFIX "macsec_linux: "
32341618Scy
33341618Scy#define UNUSED_SCI 0xffffffffffffffff
34341618Scy
35341618Scystruct cb_arg {
36341618Scy	struct macsec_drv_data *drv;
37341618Scy	u32 *pn;
38341618Scy	int ifindex;
39341618Scy	u8 txsa;
40341618Scy	u8 rxsa;
41341618Scy	u64 rxsci;
42341618Scy};
43341618Scy
44341618Scystruct macsec_genl_ctx {
45341618Scy	struct nl_sock *sk;
46341618Scy	int macsec_genl_id;
47341618Scy	struct cb_arg cb_arg;
48341618Scy};
49341618Scy
50341618Scystruct macsec_drv_data {
51341618Scy	struct driver_wired_common_data common;
52341618Scy	struct rtnl_link *link;
53341618Scy	struct nl_cache *link_cache;
54341618Scy	struct nl_sock *sk;
55341618Scy	struct macsec_genl_ctx ctx;
56341618Scy
57341618Scy	struct netlink_data *netlink;
58341618Scy	struct nl_handle *nl;
59341618Scy	char ifname[IFNAMSIZ + 1];
60341618Scy	int ifi;
61341618Scy	int parent_ifi;
62351611Scy	int use_pae_group_addr;
63341618Scy
64341618Scy	Boolean created_link;
65341618Scy
66341618Scy	Boolean controlled_port_enabled;
67341618Scy	Boolean controlled_port_enabled_set;
68341618Scy
69341618Scy	Boolean protect_frames;
70341618Scy	Boolean protect_frames_set;
71341618Scy
72341618Scy	Boolean encrypt;
73341618Scy	Boolean encrypt_set;
74341618Scy
75341618Scy	Boolean replay_protect;
76341618Scy	Boolean replay_protect_set;
77341618Scy
78341618Scy	u32 replay_window;
79341618Scy
80341618Scy	u8 encoding_sa;
81341618Scy	Boolean encoding_sa_set;
82341618Scy};
83341618Scy
84341618Scy
85341618Scystatic int dump_callback(struct nl_msg *msg, void *argp);
86341618Scy
87341618Scy
88341618Scystatic struct nl_msg * msg_prepare(enum macsec_nl_commands cmd,
89341618Scy				   const struct macsec_genl_ctx *ctx,
90341618Scy				   unsigned int ifindex)
91341618Scy{
92341618Scy	struct nl_msg *msg;
93341618Scy
94341618Scy	msg = nlmsg_alloc();
95341618Scy	if (!msg) {
96341618Scy		wpa_printf(MSG_ERROR, DRV_PREFIX "failed to alloc message");
97341618Scy		return NULL;
98341618Scy	}
99341618Scy
100341618Scy	if (!genlmsg_put(msg, 0, 0, ctx->macsec_genl_id, 0, 0, cmd, 0)) {
101341618Scy		wpa_printf(MSG_ERROR, DRV_PREFIX "failed to put header");
102341618Scy		goto nla_put_failure;
103341618Scy	}
104341618Scy
105341618Scy	NLA_PUT_U32(msg, MACSEC_ATTR_IFINDEX, ifindex);
106341618Scy
107341618Scy	return msg;
108341618Scy
109341618Scynla_put_failure:
110341618Scy	nlmsg_free(msg);
111341618Scy	return NULL;
112341618Scy}
113341618Scy
114341618Scy
115341618Scystatic int nla_put_rxsc_config(struct nl_msg *msg, u64 sci)
116341618Scy{
117341618Scy	struct nlattr *nest = nla_nest_start(msg, MACSEC_ATTR_RXSC_CONFIG);
118341618Scy
119341618Scy	if (!nest)
120341618Scy		return -1;
121341618Scy
122341618Scy	NLA_PUT_U64(msg, MACSEC_RXSC_ATTR_SCI, sci);
123341618Scy
124341618Scy	nla_nest_end(msg, nest);
125341618Scy
126341618Scy	return 0;
127341618Scy
128341618Scynla_put_failure:
129341618Scy	return -1;
130341618Scy}
131341618Scy
132341618Scy
133341618Scystatic int init_genl_ctx(struct macsec_drv_data *drv)
134341618Scy{
135341618Scy	struct macsec_genl_ctx *ctx = &drv->ctx;
136341618Scy
137341618Scy	ctx->sk = nl_socket_alloc();
138341618Scy	if (!ctx->sk) {
139341618Scy		wpa_printf(MSG_ERROR, DRV_PREFIX "failed to alloc genl socket");
140341618Scy		return -1;
141341618Scy	}
142341618Scy
143341618Scy	if (genl_connect(ctx->sk) < 0) {
144341618Scy		wpa_printf(MSG_ERROR,
145341618Scy			   DRV_PREFIX "connection to genl socket failed");
146341618Scy		goto out_free;
147341618Scy	}
148341618Scy
149341618Scy	ctx->macsec_genl_id = genl_ctrl_resolve(ctx->sk, "macsec");
150341618Scy	if (ctx->macsec_genl_id < 0) {
151341618Scy		wpa_printf(MSG_ERROR, DRV_PREFIX "genl resolve failed");
152341618Scy		goto out_free;
153341618Scy	}
154341618Scy
155341618Scy	memset(&ctx->cb_arg, 0, sizeof(ctx->cb_arg));
156341618Scy	ctx->cb_arg.drv = drv;
157341618Scy
158341618Scy	nl_socket_modify_cb(ctx->sk, NL_CB_VALID, NL_CB_CUSTOM, dump_callback,
159341618Scy			    &ctx->cb_arg);
160341618Scy
161341618Scy	return 0;
162341618Scy
163341618Scyout_free:
164341618Scy	nl_socket_free(ctx->sk);
165341618Scy	ctx->sk = NULL;
166341618Scy	return -1;
167341618Scy}
168341618Scy
169341618Scy
170341618Scystatic int try_commit(struct macsec_drv_data *drv)
171341618Scy{
172341618Scy	int err;
173341618Scy
174341618Scy	if (!drv->sk)
175341618Scy		return 0;
176341618Scy
177341618Scy	if (!drv->link)
178341618Scy		return 0;
179341618Scy
180341618Scy	if (drv->controlled_port_enabled_set) {
181341618Scy		struct rtnl_link *change = rtnl_link_alloc();
182341618Scy
183346981Scy		wpa_printf(MSG_DEBUG, DRV_PREFIX
184346981Scy			   "%s: try_commit controlled_port_enabled=%d",
185346981Scy			   drv->ifname, drv->controlled_port_enabled);
186341618Scy		if (!change)
187341618Scy			return -1;
188341618Scy
189341618Scy		rtnl_link_set_name(change, drv->ifname);
190341618Scy
191341618Scy		if (drv->controlled_port_enabled)
192341618Scy			rtnl_link_set_flags(change, IFF_UP);
193341618Scy		else
194341618Scy			rtnl_link_unset_flags(change, IFF_UP);
195341618Scy
196341618Scy		err = rtnl_link_change(drv->sk, change, change, 0);
197341618Scy		if (err < 0)
198341618Scy			return err;
199341618Scy
200341618Scy		rtnl_link_put(change);
201341618Scy
202341618Scy		drv->controlled_port_enabled_set = FALSE;
203341618Scy	}
204341618Scy
205346981Scy	if (drv->protect_frames_set) {
206346981Scy		wpa_printf(MSG_DEBUG, DRV_PREFIX
207346981Scy			   "%s: try_commit protect_frames=%d",
208346981Scy			   drv->ifname, drv->protect_frames);
209341618Scy		rtnl_link_macsec_set_protect(drv->link, drv->protect_frames);
210346981Scy	}
211341618Scy
212346981Scy	if (drv->encrypt_set) {
213346981Scy		wpa_printf(MSG_DEBUG, DRV_PREFIX "%s: try_commit encrypt=%d",
214346981Scy			   drv->ifname, drv->encrypt);
215341618Scy		rtnl_link_macsec_set_encrypt(drv->link, drv->encrypt);
216346981Scy	}
217341618Scy
218341618Scy	if (drv->replay_protect_set) {
219346981Scy		wpa_printf(MSG_DEBUG, DRV_PREFIX
220346981Scy			   "%s: try_commit replay_protect=%d replay_window=%d",
221346981Scy			   drv->ifname, drv->replay_protect,
222346981Scy			   drv->replay_window);
223341618Scy		rtnl_link_macsec_set_replay_protect(drv->link,
224341618Scy						    drv->replay_protect);
225341618Scy		if (drv->replay_protect)
226341618Scy			rtnl_link_macsec_set_window(drv->link,
227341618Scy						    drv->replay_window);
228341618Scy	}
229341618Scy
230346981Scy	if (drv->encoding_sa_set) {
231346981Scy		wpa_printf(MSG_DEBUG, DRV_PREFIX
232346981Scy			   "%s: try_commit encoding_sa=%d",
233346981Scy			   drv->ifname, drv->encoding_sa);
234341618Scy		rtnl_link_macsec_set_encoding_sa(drv->link, drv->encoding_sa);
235346981Scy	}
236341618Scy
237341618Scy	err = rtnl_link_add(drv->sk, drv->link, 0);
238341618Scy	if (err < 0)
239341618Scy		return err;
240341618Scy
241341618Scy	drv->protect_frames_set = FALSE;
242341618Scy	drv->encrypt_set = FALSE;
243341618Scy	drv->replay_protect_set = FALSE;
244341618Scy
245341618Scy	return 0;
246341618Scy}
247341618Scy
248341618Scy
249341618Scystatic void macsec_drv_wpa_deinit(void *priv)
250341618Scy{
251341618Scy	struct macsec_drv_data *drv = priv;
252341618Scy
253341618Scy	driver_wired_deinit_common(&drv->common);
254341618Scy	os_free(drv);
255341618Scy}
256341618Scy
257341618Scy
258341618Scystatic int macsec_check_macsec(void)
259341618Scy{
260341618Scy	struct nl_sock *sk;
261341618Scy	int err = -1;
262341618Scy
263341618Scy	sk = nl_socket_alloc();
264341618Scy	if (!sk) {
265341618Scy		wpa_printf(MSG_ERROR, DRV_PREFIX "failed to alloc genl socket");
266341618Scy		return -1;
267341618Scy	}
268341618Scy
269341618Scy	if (genl_connect(sk) < 0) {
270341618Scy		wpa_printf(MSG_ERROR,
271341618Scy			   DRV_PREFIX "connection to genl socket failed");
272341618Scy		goto out_free;
273341618Scy	}
274341618Scy
275341618Scy	if (genl_ctrl_resolve(sk, "macsec") < 0) {
276341618Scy		wpa_printf(MSG_ERROR,
277341618Scy			   DRV_PREFIX "genl resolve failed - macsec kernel module not present?");
278341618Scy		goto out_free;
279341618Scy	}
280341618Scy
281341618Scy	err = 0;
282341618Scy
283341618Scyout_free:
284341618Scy	nl_socket_free(sk);
285341618Scy	return err;
286341618Scy}
287341618Scy
288341618Scy
289341618Scystatic void * macsec_drv_wpa_init(void *ctx, const char *ifname)
290341618Scy{
291341618Scy	struct macsec_drv_data *drv;
292341618Scy
293341618Scy	if (macsec_check_macsec() < 0)
294341618Scy		return NULL;
295341618Scy
296341618Scy	drv = os_zalloc(sizeof(*drv));
297341618Scy	if (!drv)
298341618Scy		return NULL;
299341618Scy
300341618Scy	if (driver_wired_init_common(&drv->common, ifname, ctx) < 0) {
301341618Scy		os_free(drv);
302341618Scy		return NULL;
303341618Scy	}
304341618Scy
305341618Scy	return drv;
306341618Scy}
307341618Scy
308341618Scy
309341618Scystatic int macsec_drv_macsec_init(void *priv, struct macsec_init_params *params)
310341618Scy{
311341618Scy	struct macsec_drv_data *drv = priv;
312341618Scy	int err;
313341618Scy
314341618Scy	wpa_printf(MSG_DEBUG, "%s", __func__);
315341618Scy
316341618Scy	drv->sk = nl_socket_alloc();
317341618Scy	if (!drv->sk)
318341618Scy		return -1;
319341618Scy
320341618Scy	err = nl_connect(drv->sk, NETLINK_ROUTE);
321341618Scy	if (err < 0) {
322341618Scy		wpa_printf(MSG_ERROR, DRV_PREFIX
323341618Scy			   "Unable to connect NETLINK_ROUTE socket: %s",
324341618Scy			   strerror(errno));
325341618Scy		goto sock;
326341618Scy	}
327341618Scy
328341618Scy	err = rtnl_link_alloc_cache(drv->sk, AF_UNSPEC, &drv->link_cache);
329341618Scy	if (err < 0) {
330341618Scy		wpa_printf(MSG_ERROR, DRV_PREFIX "Unable to get link cache: %s",
331341618Scy			   strerror(errno));
332341618Scy		goto sock;
333341618Scy	}
334341618Scy
335341618Scy	drv->parent_ifi = rtnl_link_name2i(drv->link_cache, drv->common.ifname);
336341618Scy	if (drv->parent_ifi == 0) {
337341618Scy		wpa_printf(MSG_ERROR, DRV_PREFIX
338341618Scy			   "couldn't find ifindex for interface %s",
339341618Scy			   drv->common.ifname);
340341618Scy		goto cache;
341341618Scy	}
342346981Scy	wpa_printf(MSG_DEBUG, DRV_PREFIX "ifname=%s parent_ifi=%d",
343346981Scy		   drv->common.ifname, drv->parent_ifi);
344341618Scy
345341618Scy	err = init_genl_ctx(drv);
346341618Scy	if (err < 0)
347341618Scy		goto cache;
348341618Scy
349341618Scy	return 0;
350341618Scy
351341618Scycache:
352341618Scy	nl_cache_free(drv->link_cache);
353341618Scy	drv->link_cache = NULL;
354341618Scysock:
355341618Scy	nl_socket_free(drv->sk);
356341618Scy	drv->sk = NULL;
357341618Scy	return -1;
358341618Scy}
359341618Scy
360341618Scy
361341618Scystatic int macsec_drv_macsec_deinit(void *priv)
362341618Scy{
363341618Scy	struct macsec_drv_data *drv = priv;
364341618Scy
365341618Scy	wpa_printf(MSG_DEBUG, "%s", __func__);
366341618Scy
367341618Scy	if (drv->sk)
368341618Scy		nl_socket_free(drv->sk);
369341618Scy	drv->sk = NULL;
370341618Scy
371341618Scy	if (drv->link_cache)
372341618Scy		nl_cache_free(drv->link_cache);
373341618Scy	drv->link_cache = NULL;
374341618Scy
375341618Scy	if (drv->ctx.sk)
376341618Scy		nl_socket_free(drv->ctx.sk);
377341618Scy
378341618Scy	return 0;
379341618Scy}
380341618Scy
381341618Scy
382341618Scystatic int macsec_drv_get_capability(void *priv, enum macsec_cap *cap)
383341618Scy{
384341618Scy	wpa_printf(MSG_DEBUG, "%s", __func__);
385341618Scy
386341618Scy	*cap = MACSEC_CAP_INTEG_AND_CONF;
387341618Scy
388341618Scy	return 0;
389341618Scy}
390341618Scy
391341618Scy
392341618Scy/**
393341618Scy * macsec_drv_enable_protect_frames - Set protect frames status
394341618Scy * @priv: Private driver interface data
395341618Scy * @enabled: TRUE = protect frames enabled
396341618Scy *           FALSE = protect frames disabled
397341618Scy * Returns: 0 on success, -1 on failure (or if not supported)
398341618Scy */
399341618Scystatic int macsec_drv_enable_protect_frames(void *priv, Boolean enabled)
400341618Scy{
401341618Scy	struct macsec_drv_data *drv = priv;
402341618Scy
403341618Scy	wpa_printf(MSG_DEBUG, "%s -> %s", __func__, enabled ? "TRUE" : "FALSE");
404341618Scy
405341618Scy	drv->protect_frames_set = TRUE;
406341618Scy	drv->protect_frames = enabled;
407341618Scy
408341618Scy	return try_commit(drv);
409341618Scy}
410341618Scy
411341618Scy
412341618Scy/**
413341618Scy * macsec_drv_enable_encrypt - Set protect frames status
414341618Scy * @priv: Private driver interface data
415341618Scy * @enabled: TRUE = protect frames enabled
416341618Scy *           FALSE = protect frames disabled
417341618Scy * Returns: 0 on success, -1 on failure (or if not supported)
418341618Scy */
419341618Scystatic int macsec_drv_enable_encrypt(void *priv, Boolean enabled)
420341618Scy{
421341618Scy	struct macsec_drv_data *drv = priv;
422341618Scy
423341618Scy	wpa_printf(MSG_DEBUG, "%s -> %s", __func__, enabled ? "TRUE" : "FALSE");
424341618Scy
425341618Scy	drv->encrypt_set = TRUE;
426341618Scy	drv->encrypt = enabled;
427341618Scy
428341618Scy	return try_commit(drv);
429341618Scy}
430341618Scy
431341618Scy
432341618Scy/**
433341618Scy * macsec_drv_set_replay_protect - Set replay protect status and window size
434341618Scy * @priv: Private driver interface data
435341618Scy * @enabled: TRUE = replay protect enabled
436341618Scy *           FALSE = replay protect disabled
437341618Scy * @window: replay window size, valid only when replay protect enabled
438341618Scy * Returns: 0 on success, -1 on failure (or if not supported)
439341618Scy */
440341618Scystatic int macsec_drv_set_replay_protect(void *priv, Boolean enabled,
441341618Scy					 u32 window)
442341618Scy{
443341618Scy	struct macsec_drv_data *drv = priv;
444341618Scy
445341618Scy	wpa_printf(MSG_DEBUG, "%s -> %s, %u", __func__,
446341618Scy		   enabled ? "TRUE" : "FALSE", window);
447341618Scy
448341618Scy	drv->replay_protect_set = TRUE;
449341618Scy	drv->replay_protect = enabled;
450341618Scy	if (enabled)
451341618Scy		drv->replay_window = window;
452341618Scy
453341618Scy	return try_commit(drv);
454341618Scy}
455341618Scy
456341618Scy
457341618Scy/**
458341618Scy * macsec_drv_set_current_cipher_suite - Set current cipher suite
459341618Scy * @priv: Private driver interface data
460341618Scy * @cs: EUI64 identifier
461341618Scy * Returns: 0 on success, -1 on failure (or if not supported)
462341618Scy */
463341618Scystatic int macsec_drv_set_current_cipher_suite(void *priv, u64 cs)
464341618Scy{
465341618Scy	wpa_printf(MSG_DEBUG, "%s -> %016" PRIx64, __func__, cs);
466341618Scy	return 0;
467341618Scy}
468341618Scy
469341618Scy
470341618Scy/**
471341618Scy * macsec_drv_enable_controlled_port - Set controlled port status
472341618Scy * @priv: Private driver interface data
473341618Scy * @enabled: TRUE = controlled port enabled
474341618Scy *           FALSE = controlled port disabled
475341618Scy * Returns: 0 on success, -1 on failure (or if not supported)
476341618Scy */
477341618Scystatic int macsec_drv_enable_controlled_port(void *priv, Boolean enabled)
478341618Scy{
479341618Scy	struct macsec_drv_data *drv = priv;
480341618Scy
481341618Scy	wpa_printf(MSG_DEBUG, "%s -> %s", __func__, enabled ? "TRUE" : "FALSE");
482341618Scy
483341618Scy	drv->controlled_port_enabled = enabled;
484341618Scy	drv->controlled_port_enabled_set = TRUE;
485341618Scy
486341618Scy	return try_commit(drv);
487341618Scy}
488341618Scy
489341618Scy
490341618Scystatic struct nla_policy sa_policy[MACSEC_SA_ATTR_MAX + 1] = {
491341618Scy	[MACSEC_SA_ATTR_AN] = { .type = NLA_U8 },
492341618Scy	[MACSEC_SA_ATTR_ACTIVE] = { .type = NLA_U8 },
493341618Scy	[MACSEC_SA_ATTR_PN] = { .type = NLA_U32 },
494341618Scy	[MACSEC_SA_ATTR_KEYID] = { .type = NLA_BINARY },
495341618Scy};
496341618Scy
497341618Scystatic struct nla_policy sc_policy[MACSEC_RXSC_ATTR_MAX + 1] = {
498341618Scy	[MACSEC_RXSC_ATTR_SCI] = { .type = NLA_U64 },
499341618Scy	[MACSEC_RXSC_ATTR_ACTIVE] = { .type = NLA_U8 },
500341618Scy	[MACSEC_RXSC_ATTR_SA_LIST] = { .type = NLA_NESTED },
501341618Scy};
502341618Scy
503341618Scystatic struct nla_policy main_policy[MACSEC_ATTR_MAX + 1] = {
504341618Scy	[MACSEC_ATTR_IFINDEX] = { .type = NLA_U32 },
505341618Scy	[MACSEC_ATTR_SECY] = { .type = NLA_NESTED },
506341618Scy	[MACSEC_ATTR_TXSA_LIST] = { .type = NLA_NESTED },
507341618Scy	[MACSEC_ATTR_RXSC_LIST] = { .type = NLA_NESTED },
508341618Scy};
509341618Scy
510341618Scystatic int dump_callback(struct nl_msg *msg, void *argp)
511341618Scy{
512341618Scy	struct nlmsghdr *ret_hdr = nlmsg_hdr(msg);
513341618Scy	struct nlattr *tb_msg[MACSEC_ATTR_MAX + 1];
514341618Scy	struct cb_arg *arg = (struct cb_arg *) argp;
515341618Scy	struct genlmsghdr *gnlh = (struct genlmsghdr *) nlmsg_data(ret_hdr);
516341618Scy	int err;
517341618Scy
518341618Scy	if (ret_hdr->nlmsg_type != arg->drv->ctx.macsec_genl_id)
519341618Scy		return 0;
520341618Scy
521341618Scy	err = nla_parse(tb_msg, MACSEC_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
522341618Scy			genlmsg_attrlen(gnlh, 0), main_policy);
523341618Scy	if (err < 0)
524341618Scy		return 0;
525341618Scy
526341618Scy	if (!tb_msg[MACSEC_ATTR_IFINDEX])
527341618Scy		return 0;
528341618Scy
529341618Scy	if (nla_get_u32(tb_msg[MACSEC_ATTR_IFINDEX]) != (u32) arg->ifindex)
530341618Scy		return 0;
531341618Scy
532341618Scy	if (arg->txsa < 4 && !tb_msg[MACSEC_ATTR_TXSA_LIST]) {
533341618Scy		return 0;
534341618Scy	} else if (arg->txsa < 4) {
535341618Scy		struct nlattr *nla;
536341618Scy		int rem;
537341618Scy
538341618Scy		nla_for_each_nested(nla, tb_msg[MACSEC_ATTR_TXSA_LIST], rem) {
539341618Scy			struct nlattr *tb[MACSEC_SA_ATTR_MAX + 1];
540341618Scy
541341618Scy			err = nla_parse_nested(tb, MACSEC_SA_ATTR_MAX, nla,
542341618Scy					       sa_policy);
543341618Scy			if (err < 0)
544341618Scy				continue;
545341618Scy			if (!tb[MACSEC_SA_ATTR_AN])
546341618Scy				continue;
547341618Scy			if (nla_get_u8(tb[MACSEC_SA_ATTR_AN]) != arg->txsa)
548341618Scy				continue;
549341618Scy			if (!tb[MACSEC_SA_ATTR_PN])
550341618Scy				return 0;
551341618Scy			*arg->pn = nla_get_u32(tb[MACSEC_SA_ATTR_PN]);
552341618Scy			return 0;
553341618Scy		}
554341618Scy
555341618Scy		return 0;
556341618Scy	}
557341618Scy
558341618Scy	if (arg->rxsci == UNUSED_SCI)
559341618Scy		return 0;
560341618Scy
561341618Scy	if (tb_msg[MACSEC_ATTR_RXSC_LIST]) {
562341618Scy		struct nlattr *nla;
563341618Scy		int rem;
564341618Scy
565341618Scy		nla_for_each_nested(nla, tb_msg[MACSEC_ATTR_RXSC_LIST], rem) {
566341618Scy			struct nlattr *tb[MACSEC_RXSC_ATTR_MAX + 1];
567341618Scy
568341618Scy			err = nla_parse_nested(tb, MACSEC_RXSC_ATTR_MAX, nla,
569341618Scy					       sc_policy);
570341618Scy			if (err < 0)
571341618Scy				return 0;
572341618Scy			if (!tb[MACSEC_RXSC_ATTR_SCI])
573341618Scy				continue;
574341618Scy			if (nla_get_u64(tb[MACSEC_RXSC_ATTR_SCI]) != arg->rxsci)
575341618Scy				continue;
576341618Scy			if (!tb[MACSEC_RXSC_ATTR_SA_LIST])
577341618Scy				return 0;
578341618Scy
579341618Scy			nla_for_each_nested(nla, tb[MACSEC_RXSC_ATTR_SA_LIST],
580341618Scy					    rem) {
581341618Scy				struct nlattr *tb_sa[MACSEC_SA_ATTR_MAX + 1];
582341618Scy
583341618Scy				err = nla_parse_nested(tb_sa,
584341618Scy						       MACSEC_SA_ATTR_MAX, nla,
585341618Scy						       sa_policy);
586341618Scy				if (err < 0)
587341618Scy					continue;
588341618Scy				if (!tb_sa[MACSEC_SA_ATTR_AN])
589341618Scy					continue;
590341618Scy				if (nla_get_u8(tb_sa[MACSEC_SA_ATTR_AN]) !=
591341618Scy				    arg->rxsa)
592341618Scy					continue;
593341618Scy				if (!tb_sa[MACSEC_SA_ATTR_PN])
594341618Scy					return 0;
595341618Scy				*arg->pn =
596341618Scy					nla_get_u32(tb_sa[MACSEC_SA_ATTR_PN]);
597341618Scy
598341618Scy				return 0;
599341618Scy			}
600341618Scy
601341618Scy			return 0;
602341618Scy		}
603341618Scy
604341618Scy		return 0;
605341618Scy	}
606341618Scy
607341618Scy	return 0;
608341618Scy}
609341618Scy
610341618Scy
611341618Scystatic int nl_send_recv(struct nl_sock *sk, struct nl_msg *msg)
612341618Scy{
613341618Scy	int ret;
614341618Scy
615341618Scy	ret = nl_send_auto_complete(sk, msg);
616341618Scy	if (ret < 0) {
617341618Scy		wpa_printf(MSG_ERROR, DRV_PREFIX "%s: failed to send: %d (%s)",
618341618Scy			   __func__, ret, nl_geterror(-ret));
619341618Scy		return ret;
620341618Scy	}
621341618Scy
622341618Scy	ret = nl_recvmsgs_default(sk);
623341618Scy	if (ret < 0) {
624341618Scy		wpa_printf(MSG_ERROR, DRV_PREFIX "%s: failed to recv: %d (%s)",
625341618Scy			   __func__, ret, nl_geterror(-ret));
626341618Scy	}
627341618Scy
628341618Scy	return ret;
629341618Scy}
630341618Scy
631341618Scy
632341618Scystatic int do_dump(struct macsec_drv_data *drv, u8 txsa, u64 rxsci, u8 rxsa,
633341618Scy		   u32 *pn)
634341618Scy{
635341618Scy	struct macsec_genl_ctx *ctx = &drv->ctx;
636341618Scy	struct nl_msg *msg;
637341618Scy	int ret = 1;
638341618Scy
639341618Scy	ctx->cb_arg.ifindex = drv->ifi;
640341618Scy	ctx->cb_arg.rxsci = rxsci;
641341618Scy	ctx->cb_arg.rxsa = rxsa;
642341618Scy	ctx->cb_arg.txsa = txsa;
643341618Scy	ctx->cb_arg.pn = pn;
644341618Scy
645341618Scy	msg = nlmsg_alloc();
646341618Scy	if (!msg) {
647341618Scy		wpa_printf(MSG_ERROR, DRV_PREFIX "%s: failed to alloc message",
648341618Scy			   __func__);
649341618Scy		return 1;
650341618Scy	}
651341618Scy
652341618Scy	if (!genlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ, ctx->macsec_genl_id, 0,
653341618Scy			 NLM_F_DUMP, MACSEC_CMD_GET_TXSC, 0)) {
654341618Scy		wpa_printf(MSG_ERROR, DRV_PREFIX "%s: failed to put header",
655341618Scy			   __func__);
656341618Scy		goto out_free_msg;
657341618Scy	}
658341618Scy
659341618Scy	ret = nl_send_recv(ctx->sk, msg);
660341618Scy	if (ret < 0)
661341618Scy		wpa_printf(MSG_ERROR,
662341618Scy			   DRV_PREFIX "failed to communicate: %d (%s)",
663341618Scy			   ret, nl_geterror(-ret));
664341618Scy
665341618Scy	ctx->cb_arg.pn = NULL;
666341618Scy
667341618Scyout_free_msg:
668341618Scy	nlmsg_free(msg);
669341618Scy	return ret;
670341618Scy}
671341618Scy
672341618Scy
673341618Scy/**
674341618Scy * macsec_drv_get_receive_lowest_pn - Get receive lowest PN
675341618Scy * @priv: Private driver interface data
676341618Scy * @sa: secure association
677341618Scy * Returns: 0 on success, -1 on failure (or if not supported)
678341618Scy */
679341618Scystatic int macsec_drv_get_receive_lowest_pn(void *priv, struct receive_sa *sa)
680341618Scy{
681341618Scy	struct macsec_drv_data *drv = priv;
682341618Scy	int err;
683341618Scy
684341618Scy	wpa_printf(MSG_DEBUG, DRV_PREFIX "%s", __func__);
685341618Scy
686341618Scy	err = do_dump(drv, 0xff, mka_sci_u64(&sa->sc->sci), sa->an,
687341618Scy		      &sa->lowest_pn);
688341618Scy	wpa_printf(MSG_DEBUG, DRV_PREFIX "%s: result %d", __func__,
689341618Scy		   sa->lowest_pn);
690341618Scy
691341618Scy	return err;
692341618Scy}
693341618Scy
694341618Scy
695341618Scy/**
696346981Scy * macsec_drv_set_receive_lowest_pn - Set receive lowest PN
697346981Scy * @priv: Private driver interface data
698346981Scy * @sa: secure association
699346981Scy * Returns: 0 on success, -1 on failure (or if not supported)
700346981Scy */
701346981Scystatic int macsec_drv_set_receive_lowest_pn(void *priv, struct receive_sa *sa)
702346981Scy{
703346981Scy	struct macsec_drv_data *drv = priv;
704346981Scy	struct macsec_genl_ctx *ctx = &drv->ctx;
705346981Scy	struct nl_msg *msg;
706346981Scy	struct nlattr *nest;
707346981Scy	int ret = -1;
708346981Scy
709346981Scy	wpa_printf(MSG_DEBUG,
710346981Scy		   DRV_PREFIX "%s: set_receive_lowest_pn -> %d: %d",
711346981Scy		   drv->ifname, sa->an, sa->next_pn);
712346981Scy
713346981Scy	msg = msg_prepare(MACSEC_CMD_UPD_RXSA, ctx, drv->ifi);
714346981Scy	if (!msg)
715346981Scy		return ret;
716346981Scy
717346981Scy	nest = nla_nest_start(msg, MACSEC_ATTR_SA_CONFIG);
718346981Scy	if (!nest)
719346981Scy		goto nla_put_failure;
720346981Scy
721346981Scy	NLA_PUT_U8(msg, MACSEC_SA_ATTR_AN, sa->an);
722346981Scy	NLA_PUT_U32(msg, MACSEC_SA_ATTR_PN, sa->next_pn);
723346981Scy
724346981Scy	nla_nest_end(msg, nest);
725346981Scy
726346981Scy	ret = nl_send_recv(ctx->sk, msg);
727346981Scy	if (ret < 0) {
728346981Scy		wpa_printf(MSG_ERROR,
729346981Scy			   DRV_PREFIX "failed to communicate: %d (%s)",
730346981Scy			   ret, nl_geterror(-ret));
731346981Scy	}
732346981Scy
733346981Scynla_put_failure:
734346981Scy	nlmsg_free(msg);
735346981Scy	return ret;
736346981Scy}
737346981Scy
738346981Scy
739346981Scy/**
740341618Scy * macsec_drv_get_transmit_next_pn - Get transmit next PN
741341618Scy * @priv: Private driver interface data
742341618Scy * @sa: secure association
743341618Scy * Returns: 0 on success, -1 on failure (or if not supported)
744341618Scy */
745341618Scystatic int macsec_drv_get_transmit_next_pn(void *priv, struct transmit_sa *sa)
746341618Scy{
747341618Scy	struct macsec_drv_data *drv = priv;
748341618Scy	int err;
749341618Scy
750341618Scy	wpa_printf(MSG_DEBUG, "%s", __func__);
751341618Scy
752341618Scy	err = do_dump(drv, sa->an, UNUSED_SCI, 0xff, &sa->next_pn);
753341618Scy	wpa_printf(MSG_DEBUG, DRV_PREFIX "%s: err %d result %d", __func__, err,
754341618Scy		   sa->next_pn);
755341618Scy	return err;
756341618Scy}
757341618Scy
758341618Scy
759341618Scy/**
760341618Scy * macsec_drv_set_transmit_next_pn - Set transmit next pn
761341618Scy * @priv: Private driver interface data
762341618Scy * @sa: secure association
763341618Scy * Returns: 0 on success, -1 on failure (or if not supported)
764341618Scy */
765341618Scystatic int macsec_drv_set_transmit_next_pn(void *priv, struct transmit_sa *sa)
766341618Scy{
767341618Scy	struct macsec_drv_data *drv = priv;
768341618Scy	struct macsec_genl_ctx *ctx = &drv->ctx;
769341618Scy	struct nl_msg *msg;
770341618Scy	struct nlattr *nest;
771341618Scy	int ret = -1;
772341618Scy
773341618Scy	wpa_printf(MSG_DEBUG, "%s -> %d: %d", __func__, sa->an, sa->next_pn);
774341618Scy
775341618Scy	msg = msg_prepare(MACSEC_CMD_UPD_TXSA, ctx, drv->ifi);
776341618Scy	if (!msg)
777341618Scy		return ret;
778341618Scy
779341618Scy	nest = nla_nest_start(msg, MACSEC_ATTR_SA_CONFIG);
780341618Scy	if (!nest)
781341618Scy		goto nla_put_failure;
782341618Scy
783341618Scy	NLA_PUT_U8(msg, MACSEC_SA_ATTR_AN, sa->an);
784341618Scy	NLA_PUT_U32(msg, MACSEC_SA_ATTR_PN, sa->next_pn);
785341618Scy
786341618Scy	nla_nest_end(msg, nest);
787341618Scy
788341618Scy	ret = nl_send_recv(ctx->sk, msg);
789341618Scy	if (ret < 0) {
790341618Scy		wpa_printf(MSG_ERROR,
791341618Scy			   DRV_PREFIX "failed to communicate: %d (%s)",
792341618Scy			   ret, nl_geterror(-ret));
793341618Scy	}
794341618Scy
795341618Scynla_put_failure:
796341618Scy	nlmsg_free(msg);
797341618Scy	return ret;
798341618Scy}
799341618Scy
800341618Scy
801341618Scy#define SCISTR MACSTR "::%hx"
802341618Scy#define SCI2STR(addr, port) MAC2STR(addr), htons(port)
803341618Scy
804341618Scy/**
805341618Scy * macsec_drv_create_receive_sc - Create secure channel for receiving
806341618Scy * @priv: Private driver interface data
807341618Scy * @sc: secure channel
808341618Scy * @sci_addr: secure channel identifier - address
809341618Scy * @sci_port: secure channel identifier - port
810341618Scy * @conf_offset: confidentiality offset (0, 30, or 50)
811341618Scy * @validation: frame validation policy (0 = Disabled, 1 = Checked,
812341618Scy *	2 = Strict)
813341618Scy * Returns: 0 on success, -1 on failure (or if not supported)
814341618Scy */
815341618Scystatic int macsec_drv_create_receive_sc(void *priv, struct receive_sc *sc,
816341618Scy					unsigned int conf_offset,
817341618Scy					int validation)
818341618Scy{
819341618Scy	struct macsec_drv_data *drv = priv;
820341618Scy	struct macsec_genl_ctx *ctx = &drv->ctx;
821341618Scy	struct nl_msg *msg;
822341618Scy	int ret = -1;
823341618Scy
824346981Scy	wpa_printf(MSG_DEBUG, DRV_PREFIX "%s: create_receive_sc -> " SCISTR
825346981Scy		   " (conf_offset=%u validation=%d)",
826346981Scy		   drv->ifname, SCI2STR(sc->sci.addr, sc->sci.port),
827346981Scy		   conf_offset, validation);
828341618Scy
829341618Scy	msg = msg_prepare(MACSEC_CMD_ADD_RXSC, ctx, drv->ifi);
830341618Scy	if (!msg)
831341618Scy		return ret;
832341618Scy
833341618Scy	if (nla_put_rxsc_config(msg, mka_sci_u64(&sc->sci)))
834341618Scy		goto nla_put_failure;
835341618Scy
836341618Scy	ret = nl_send_recv(ctx->sk, msg);
837341618Scy	if (ret < 0) {
838341618Scy		wpa_printf(MSG_ERROR,
839341618Scy			   DRV_PREFIX "%s: failed to communicate: %d (%s)",
840341618Scy			   __func__, ret, nl_geterror(-ret));
841341618Scy	}
842341618Scy
843341618Scynla_put_failure:
844341618Scy	nlmsg_free(msg);
845341618Scy	return ret;
846341618Scy}
847341618Scy
848341618Scy
849341618Scy/**
850341618Scy * macsec_drv_delete_receive_sc - Delete secure connection for receiving
851341618Scy * @priv: private driver interface data from init()
852341618Scy * @sc: secure channel
853341618Scy * Returns: 0 on success, -1 on failure
854341618Scy */
855341618Scystatic int macsec_drv_delete_receive_sc(void *priv, struct receive_sc *sc)
856341618Scy{
857341618Scy	struct macsec_drv_data *drv = priv;
858341618Scy	struct macsec_genl_ctx *ctx = &drv->ctx;
859341618Scy	struct nl_msg *msg;
860341618Scy	int ret = -1;
861341618Scy
862346981Scy	wpa_printf(MSG_DEBUG, DRV_PREFIX "%s: delete_receive_sc -> " SCISTR,
863346981Scy		   drv->ifname, SCI2STR(sc->sci.addr, sc->sci.port));
864341618Scy
865341618Scy	msg = msg_prepare(MACSEC_CMD_DEL_RXSC, ctx, drv->ifi);
866341618Scy	if (!msg)
867341618Scy		return ret;
868341618Scy
869341618Scy	if (nla_put_rxsc_config(msg, mka_sci_u64(&sc->sci)))
870341618Scy		goto nla_put_failure;
871341618Scy
872341618Scy	ret = nl_send_recv(ctx->sk, msg);
873341618Scy	if (ret < 0) {
874341618Scy		wpa_printf(MSG_ERROR,
875341618Scy			   DRV_PREFIX "%s: failed to communicate: %d (%s)",
876341618Scy			   __func__, ret, nl_geterror(-ret));
877341618Scy	}
878341618Scy
879341618Scynla_put_failure:
880341618Scy	nlmsg_free(msg);
881341618Scy	return ret;
882341618Scy}
883341618Scy
884341618Scy
885341618Scy/**
886341618Scy * macsec_drv_create_receive_sa - Create secure association for receive
887341618Scy * @priv: private driver interface data from init()
888341618Scy * @sa: secure association
889341618Scy * Returns: 0 on success, -1 on failure
890341618Scy */
891341618Scystatic int macsec_drv_create_receive_sa(void *priv, struct receive_sa *sa)
892341618Scy{
893341618Scy	struct macsec_drv_data *drv = priv;
894341618Scy	struct macsec_genl_ctx *ctx = &drv->ctx;
895341618Scy	struct nl_msg *msg;
896341618Scy	struct nlattr *nest;
897341618Scy	int ret = -1;
898341618Scy
899346981Scy	wpa_printf(MSG_DEBUG,
900346981Scy		   DRV_PREFIX "%s: create_receive_sa -> %d on " SCISTR
901346981Scy		   " (enable_receive=%d next_pn=%u)",
902346981Scy		   drv->ifname, sa->an,
903346981Scy		   SCI2STR(sa->sc->sci.addr, sa->sc->sci.port),
904346981Scy		   sa->enable_receive, sa->next_pn);
905346981Scy	wpa_hexdump(MSG_DEBUG, DRV_PREFIX "SA keyid",
906346981Scy		    &sa->pkey->key_identifier,
907346981Scy		    sizeof(sa->pkey->key_identifier));
908346981Scy	wpa_hexdump_key(MSG_DEBUG, DRV_PREFIX "SA key",
909346981Scy			sa->pkey->key, sa->pkey->key_len);
910341618Scy
911341618Scy	msg = msg_prepare(MACSEC_CMD_ADD_RXSA, ctx, drv->ifi);
912341618Scy	if (!msg)
913341618Scy		return ret;
914341618Scy
915341618Scy	if (nla_put_rxsc_config(msg, mka_sci_u64(&sa->sc->sci)))
916341618Scy		goto nla_put_failure;
917341618Scy
918341618Scy	nest = nla_nest_start(msg, MACSEC_ATTR_SA_CONFIG);
919341618Scy	if (!nest)
920341618Scy		goto nla_put_failure;
921341618Scy
922341618Scy	NLA_PUT_U8(msg, MACSEC_SA_ATTR_AN, sa->an);
923341618Scy	NLA_PUT_U8(msg, MACSEC_SA_ATTR_ACTIVE, sa->enable_receive);
924341618Scy	NLA_PUT_U32(msg, MACSEC_SA_ATTR_PN, sa->next_pn);
925341618Scy	NLA_PUT(msg, MACSEC_SA_ATTR_KEYID, sizeof(sa->pkey->key_identifier),
926341618Scy		&sa->pkey->key_identifier);
927341618Scy	NLA_PUT(msg, MACSEC_SA_ATTR_KEY, sa->pkey->key_len, sa->pkey->key);
928341618Scy
929341618Scy	nla_nest_end(msg, nest);
930341618Scy
931341618Scy	ret = nl_send_recv(ctx->sk, msg);
932341618Scy	if (ret < 0) {
933341618Scy		wpa_printf(MSG_ERROR,
934341618Scy			   DRV_PREFIX "%s: failed to communicate: %d (%s)",
935341618Scy			   __func__, ret, nl_geterror(-ret));
936341618Scy	}
937341618Scy
938341618Scynla_put_failure:
939341618Scy	nlmsg_free(msg);
940341618Scy	return ret;
941341618Scy}
942341618Scy
943341618Scy
944341618Scy/**
945341618Scy * macsec_drv_delete_receive_sa - Delete secure association for receive
946341618Scy * @priv: private driver interface data from init()
947341618Scy * @sa: secure association
948341618Scy * Returns: 0 on success, -1 on failure
949341618Scy */
950341618Scystatic int macsec_drv_delete_receive_sa(void *priv, struct receive_sa *sa)
951341618Scy{
952341618Scy	struct macsec_drv_data *drv = priv;
953341618Scy	struct macsec_genl_ctx *ctx = &drv->ctx;
954341618Scy	struct nl_msg *msg;
955341618Scy	struct nlattr *nest;
956341618Scy	int ret = -1;
957341618Scy
958346981Scy	wpa_printf(MSG_DEBUG, DRV_PREFIX "%s: delete_receive_sa -> %d on "
959346981Scy		   SCISTR, drv->ifname, sa->an,
960341618Scy		   SCI2STR(sa->sc->sci.addr, sa->sc->sci.port));
961341618Scy
962341618Scy	msg = msg_prepare(MACSEC_CMD_DEL_RXSA, ctx, drv->ifi);
963341618Scy	if (!msg)
964341618Scy		return ret;
965341618Scy
966341618Scy	if (nla_put_rxsc_config(msg, mka_sci_u64(&sa->sc->sci)))
967341618Scy		goto nla_put_failure;
968341618Scy
969341618Scy	nest = nla_nest_start(msg, MACSEC_ATTR_SA_CONFIG);
970341618Scy	if (!nest)
971341618Scy		goto nla_put_failure;
972341618Scy
973341618Scy	NLA_PUT_U8(msg, MACSEC_SA_ATTR_AN, sa->an);
974341618Scy
975341618Scy	nla_nest_end(msg, nest);
976341618Scy
977341618Scy	ret = nl_send_recv(ctx->sk, msg);
978341618Scy	if (ret < 0) {
979341618Scy		wpa_printf(MSG_ERROR,
980341618Scy			   DRV_PREFIX "%s: failed to communicate: %d (%s)",
981341618Scy			   __func__, ret, nl_geterror(-ret));
982341618Scy	}
983341618Scy
984341618Scynla_put_failure:
985341618Scy	nlmsg_free(msg);
986341618Scy	return ret;
987341618Scy}
988341618Scy
989341618Scy
990341618Scystatic int set_active_rx_sa(const struct macsec_genl_ctx *ctx, int ifindex,
991341618Scy			    u64 sci, unsigned char an, Boolean state)
992341618Scy{
993341618Scy	struct nl_msg *msg;
994341618Scy	struct nlattr *nest;
995341618Scy	int ret = -1;
996341618Scy
997341618Scy	msg = msg_prepare(MACSEC_CMD_UPD_RXSA, ctx, ifindex);
998341618Scy	if (!msg)
999341618Scy		return ret;
1000341618Scy
1001341618Scy	if (nla_put_rxsc_config(msg, sci))
1002341618Scy		goto nla_put_failure;
1003341618Scy
1004341618Scy	nest = nla_nest_start(msg, MACSEC_ATTR_SA_CONFIG);
1005341618Scy	if (!nest)
1006341618Scy		goto nla_put_failure;
1007341618Scy
1008341618Scy	NLA_PUT_U8(msg, MACSEC_SA_ATTR_AN, an);
1009341618Scy	NLA_PUT_U8(msg, MACSEC_SA_ATTR_ACTIVE, !!state);
1010341618Scy
1011341618Scy	nla_nest_end(msg, nest);
1012341618Scy
1013341618Scy	ret = nl_send_recv(ctx->sk, msg);
1014341618Scy	if (ret < 0)
1015341618Scy		wpa_printf(MSG_ERROR,
1016341618Scy			   DRV_PREFIX "%s: failed to communicate: %d (%s)",
1017341618Scy			   __func__, ret, nl_geterror(-ret));
1018341618Scy
1019341618Scynla_put_failure:
1020341618Scy	nlmsg_free(msg);
1021341618Scy	return ret;
1022341618Scy}
1023341618Scy
1024341618Scy
1025341618Scy/**
1026341618Scy * macsec_drv_enable_receive_sa - Enable the SA for receive
1027341618Scy * @priv: private driver interface data from init()
1028341618Scy * @sa: secure association
1029341618Scy * Returns: 0 on success, -1 on failure
1030341618Scy */
1031341618Scystatic int macsec_drv_enable_receive_sa(void *priv, struct receive_sa *sa)
1032341618Scy{
1033341618Scy	struct macsec_drv_data *drv = priv;
1034341618Scy	struct macsec_genl_ctx *ctx = &drv->ctx;
1035341618Scy
1036346981Scy	wpa_printf(MSG_DEBUG, DRV_PREFIX "%s: enable_receive_sa -> %d on "
1037346981Scy		   SCISTR, drv->ifname, sa->an,
1038341618Scy		   SCI2STR(sa->sc->sci.addr, sa->sc->sci.port));
1039341618Scy
1040341618Scy	return set_active_rx_sa(ctx, drv->ifi, mka_sci_u64(&sa->sc->sci),
1041341618Scy				sa->an, TRUE);
1042341618Scy}
1043341618Scy
1044341618Scy
1045341618Scy/**
1046341618Scy * macsec_drv_disable_receive_sa - Disable SA for receive
1047341618Scy * @priv: private driver interface data from init()
1048341618Scy * @sa: secure association
1049341618Scy * Returns: 0 on success, -1 on failure
1050341618Scy */
1051341618Scystatic int macsec_drv_disable_receive_sa(void *priv, struct receive_sa *sa)
1052341618Scy{
1053341618Scy	struct macsec_drv_data *drv = priv;
1054341618Scy	struct macsec_genl_ctx *ctx = &drv->ctx;
1055341618Scy
1056346981Scy	wpa_printf(MSG_DEBUG, DRV_PREFIX "%s: disable_receive_sa -> %d on "
1057346981Scy		   SCISTR, drv->ifname, sa->an,
1058341618Scy		   SCI2STR(sa->sc->sci.addr, sa->sc->sci.port));
1059341618Scy
1060341618Scy	return set_active_rx_sa(ctx, drv->ifi, mka_sci_u64(&sa->sc->sci),
1061341618Scy				sa->an, FALSE);
1062341618Scy}
1063341618Scy
1064341618Scy
1065341618Scystatic struct rtnl_link * lookup_sc(struct nl_cache *cache, int parent, u64 sci)
1066341618Scy{
1067341618Scy	struct rtnl_link *needle;
1068341618Scy	void *match;
1069341618Scy
1070341618Scy	needle = rtnl_link_macsec_alloc();
1071341618Scy	if (!needle)
1072341618Scy		return NULL;
1073341618Scy
1074341618Scy	rtnl_link_set_link(needle, parent);
1075341618Scy	rtnl_link_macsec_set_sci(needle, sci);
1076341618Scy
1077341618Scy	match = nl_cache_find(cache, (struct nl_object *) needle);
1078341618Scy	rtnl_link_put(needle);
1079341618Scy
1080341618Scy	return (struct rtnl_link *) match;
1081341618Scy}
1082341618Scy
1083341618Scy
1084341618Scy/**
1085341618Scy * macsec_drv_create_transmit_sc - Create secure connection for transmit
1086341618Scy * @priv: private driver interface data from init()
1087341618Scy * @sc: secure channel
1088341618Scy * @conf_offset: confidentiality offset
1089341618Scy * Returns: 0 on success, -1 on failure
1090341618Scy */
1091341618Scystatic int macsec_drv_create_transmit_sc(
1092341618Scy	void *priv, struct transmit_sc *sc,
1093341618Scy	unsigned int conf_offset)
1094341618Scy{
1095341618Scy	struct macsec_drv_data *drv = priv;
1096341618Scy	struct rtnl_link *link;
1097341618Scy	char *ifname;
1098341618Scy	u64 sci;
1099341618Scy	int err;
1100341618Scy
1101346981Scy	wpa_printf(MSG_DEBUG, DRV_PREFIX
1102346981Scy		   "%s: create_transmit_sc -> " SCISTR " (conf_offset=%d)",
1103346981Scy		   drv->common.ifname, SCI2STR(sc->sci.addr, sc->sci.port),
1104346981Scy		   conf_offset);
1105341618Scy
1106341618Scy	if (!drv->sk) {
1107341618Scy		wpa_printf(MSG_ERROR, DRV_PREFIX "NULL rtnl socket");
1108341618Scy		return -1;
1109341618Scy	}
1110341618Scy
1111341618Scy	link = rtnl_link_macsec_alloc();
1112341618Scy	if (!link) {
1113341618Scy		wpa_printf(MSG_ERROR, DRV_PREFIX "couldn't allocate link");
1114341618Scy		return -1;
1115341618Scy	}
1116341618Scy
1117341618Scy	rtnl_link_set_link(link, drv->parent_ifi);
1118341618Scy
1119341618Scy	sci = mka_sci_u64(&sc->sci);
1120341618Scy	rtnl_link_macsec_set_sci(link, sci);
1121341618Scy
1122341618Scy	drv->created_link = TRUE;
1123341618Scy
1124341618Scy	err = rtnl_link_add(drv->sk, link, NLM_F_CREATE);
1125341618Scy	if (err == -NLE_BUSY) {
1126341618Scy		wpa_printf(MSG_INFO,
1127341618Scy			   DRV_PREFIX "link already exists, using it");
1128341618Scy		drv->created_link = FALSE;
1129341618Scy	} else if (err < 0) {
1130341618Scy		rtnl_link_put(link);
1131341618Scy		wpa_printf(MSG_ERROR, DRV_PREFIX "couldn't create link: err %d",
1132341618Scy			   err);
1133341618Scy		return err;
1134341618Scy	}
1135341618Scy
1136341618Scy	rtnl_link_put(link);
1137341618Scy
1138341618Scy	nl_cache_refill(drv->sk, drv->link_cache);
1139341618Scy	link = lookup_sc(drv->link_cache, drv->parent_ifi, sci);
1140341618Scy	if (!link) {
1141341618Scy		wpa_printf(MSG_ERROR, DRV_PREFIX "couldn't find link");
1142341618Scy		return -1;
1143341618Scy	}
1144341618Scy
1145341618Scy	drv->ifi = rtnl_link_get_ifindex(link);
1146341618Scy	ifname = rtnl_link_get_name(link);
1147346981Scy	wpa_printf(MSG_DEBUG,
1148346981Scy		   DRV_PREFIX "%s: create_transmit_sc: ifi=%d ifname=%s",
1149346981Scy		   drv->common.ifname, drv->ifi, ifname);
1150341618Scy	os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname));
1151341618Scy	rtnl_link_put(link);
1152341618Scy
1153341618Scy	drv->link = rtnl_link_macsec_alloc();
1154341618Scy	if (!drv->link) {
1155341618Scy		wpa_printf(MSG_ERROR, DRV_PREFIX "couldn't allocate link");
1156341618Scy		return -1;
1157341618Scy	}
1158341618Scy
1159341618Scy	rtnl_link_set_name(drv->link, drv->ifname);
1160341618Scy
1161341618Scy	/* In case some settings have already been done but we couldn't apply
1162341618Scy	 * them. */
1163341618Scy	return try_commit(drv);
1164341618Scy}
1165341618Scy
1166341618Scy
1167341618Scy/**
1168341618Scy * macsec_drv_delete_transmit_sc - Delete secure connection for transmit
1169341618Scy * @priv: private driver interface data from init()
1170341618Scy * @sc: secure channel
1171341618Scy * Returns: 0 on success, -1 on failure
1172341618Scy */
1173341618Scystatic int macsec_drv_delete_transmit_sc(void *priv, struct transmit_sc *sc)
1174341618Scy{
1175341618Scy	struct macsec_drv_data *drv = priv;
1176341618Scy	int err;
1177341618Scy
1178346981Scy	wpa_printf(MSG_DEBUG, DRV_PREFIX "%s: delete_transmit_sc -> " SCISTR,
1179346981Scy		   drv->ifname, SCI2STR(sc->sci.addr, sc->sci.port));
1180341618Scy
1181341618Scy	if (!drv->sk)
1182341618Scy		return 0;
1183341618Scy
1184341618Scy	if (!drv->created_link) {
1185341618Scy		rtnl_link_put(drv->link);
1186341618Scy		drv->link = NULL;
1187341618Scy		wpa_printf(MSG_DEBUG, DRV_PREFIX
1188341618Scy			   "we didn't create the link, leave it alone");
1189341618Scy		return 0;
1190341618Scy	}
1191341618Scy
1192341618Scy	err = rtnl_link_delete(drv->sk, drv->link);
1193341618Scy	if (err < 0)
1194341618Scy		wpa_printf(MSG_ERROR, DRV_PREFIX "couldn't delete link");
1195341618Scy	rtnl_link_put(drv->link);
1196341618Scy	drv->link = NULL;
1197341618Scy
1198341618Scy	return err;
1199341618Scy}
1200341618Scy
1201341618Scy
1202341618Scy/**
1203341618Scy * macsec_drv_create_transmit_sa - Create secure association for transmit
1204341618Scy * @priv: private driver interface data from init()
1205341618Scy * @sa: secure association
1206341618Scy * Returns: 0 on success, -1 on failure
1207341618Scy */
1208341618Scystatic int macsec_drv_create_transmit_sa(void *priv, struct transmit_sa *sa)
1209341618Scy{
1210341618Scy	struct macsec_drv_data *drv = priv;
1211341618Scy	struct macsec_genl_ctx *ctx = &drv->ctx;
1212341618Scy	struct nl_msg *msg;
1213341618Scy	struct nlattr *nest;
1214341618Scy	int ret = -1;
1215341618Scy
1216346981Scy	wpa_printf(MSG_DEBUG, DRV_PREFIX "%s: create_transmit_sa -> %d on "
1217346981Scy		   SCISTR " (enable_transmit=%d next_pn=%u)",
1218346981Scy		   drv->ifname, sa->an,
1219346981Scy		   SCI2STR(sa->sc->sci.addr, sa->sc->sci.port),
1220346981Scy		   sa->enable_transmit, sa->next_pn);
1221346981Scy	wpa_hexdump(MSG_DEBUG, DRV_PREFIX "SA keyid",
1222346981Scy		    &sa->pkey->key_identifier,
1223346981Scy		    sizeof(sa->pkey->key_identifier));
1224346981Scy	wpa_hexdump_key(MSG_DEBUG, DRV_PREFIX "SA key",
1225346981Scy			sa->pkey->key, sa->pkey->key_len);
1226341618Scy
1227341618Scy	msg = msg_prepare(MACSEC_CMD_ADD_TXSA, ctx, drv->ifi);
1228341618Scy	if (!msg)
1229341618Scy		return ret;
1230341618Scy
1231341618Scy	nest = nla_nest_start(msg, MACSEC_ATTR_SA_CONFIG);
1232341618Scy	if (!nest)
1233341618Scy		goto nla_put_failure;
1234341618Scy
1235341618Scy	NLA_PUT_U8(msg, MACSEC_SA_ATTR_AN, sa->an);
1236341618Scy	NLA_PUT_U32(msg, MACSEC_SA_ATTR_PN, sa->next_pn);
1237341618Scy	NLA_PUT(msg, MACSEC_SA_ATTR_KEYID, sizeof(sa->pkey->key_identifier),
1238341618Scy		&sa->pkey->key_identifier);
1239341618Scy	NLA_PUT(msg, MACSEC_SA_ATTR_KEY, sa->pkey->key_len, sa->pkey->key);
1240341618Scy	NLA_PUT_U8(msg, MACSEC_SA_ATTR_ACTIVE, sa->enable_transmit);
1241341618Scy
1242341618Scy	nla_nest_end(msg, nest);
1243341618Scy
1244341618Scy	ret = nl_send_recv(ctx->sk, msg);
1245341618Scy	if (ret < 0) {
1246341618Scy		wpa_printf(MSG_ERROR,
1247341618Scy			   DRV_PREFIX "%s: failed to communicate: %d (%s)",
1248341618Scy			   __func__, ret, nl_geterror(-ret));
1249341618Scy	}
1250341618Scy
1251341618Scynla_put_failure:
1252341618Scy	nlmsg_free(msg);
1253341618Scy	return ret;
1254341618Scy}
1255341618Scy
1256341618Scy
1257341618Scy/**
1258341618Scy * macsec_drv_delete_transmit_sa - Delete secure association for transmit
1259341618Scy * @priv: private driver interface data from init()
1260341618Scy * @sa: secure association
1261341618Scy * Returns: 0 on success, -1 on failure
1262341618Scy */
1263341618Scystatic int macsec_drv_delete_transmit_sa(void *priv, struct transmit_sa *sa)
1264341618Scy{
1265341618Scy	struct macsec_drv_data *drv = priv;
1266341618Scy	struct macsec_genl_ctx *ctx = &drv->ctx;
1267341618Scy	struct nl_msg *msg;
1268341618Scy	struct nlattr *nest;
1269341618Scy	int ret = -1;
1270341618Scy
1271346981Scy	wpa_printf(MSG_DEBUG, DRV_PREFIX "%s: delete_transmit_sa -> %d on "
1272346981Scy		   SCISTR, drv->ifname, sa->an,
1273346981Scy		   SCI2STR(sa->sc->sci.addr, sa->sc->sci.port));
1274341618Scy
1275341618Scy	msg = msg_prepare(MACSEC_CMD_DEL_TXSA, ctx, drv->ifi);
1276341618Scy	if (!msg)
1277341618Scy		return ret;
1278341618Scy
1279341618Scy	nest = nla_nest_start(msg, MACSEC_ATTR_SA_CONFIG);
1280341618Scy	if (!nest)
1281341618Scy		goto nla_put_failure;
1282341618Scy
1283341618Scy	NLA_PUT_U8(msg, MACSEC_SA_ATTR_AN, sa->an);
1284341618Scy
1285341618Scy	nla_nest_end(msg, nest);
1286341618Scy
1287341618Scy	ret = nl_send_recv(ctx->sk, msg);
1288341618Scy	if (ret < 0) {
1289341618Scy		wpa_printf(MSG_ERROR,
1290341618Scy			   DRV_PREFIX "%s: failed to communicate: %d (%s)",
1291341618Scy			   __func__, ret, nl_geterror(-ret));
1292341618Scy	}
1293341618Scy
1294341618Scynla_put_failure:
1295341618Scy	nlmsg_free(msg);
1296341618Scy	return ret;
1297341618Scy}
1298341618Scy
1299341618Scy
1300341618Scystatic int set_active_tx_sa(const struct macsec_genl_ctx *ctx, int ifindex,
1301341618Scy			    unsigned char an, Boolean state)
1302341618Scy{
1303341618Scy	struct nl_msg *msg;
1304341618Scy	struct nlattr *nest;
1305341618Scy	int ret = -1;
1306341618Scy
1307341618Scy	msg = msg_prepare(MACSEC_CMD_UPD_TXSA, ctx, ifindex);
1308341618Scy	if (!msg)
1309341618Scy		return ret;
1310341618Scy
1311341618Scy	nest = nla_nest_start(msg, MACSEC_ATTR_SA_CONFIG);
1312341618Scy	if (!nest)
1313341618Scy		goto nla_put_failure;
1314341618Scy
1315341618Scy	NLA_PUT_U8(msg, MACSEC_SA_ATTR_AN, an);
1316341618Scy	NLA_PUT_U8(msg, MACSEC_SA_ATTR_ACTIVE, !!state);
1317341618Scy
1318341618Scy	nla_nest_end(msg, nest);
1319341618Scy
1320341618Scy	ret = nl_send_recv(ctx->sk, msg);
1321341618Scy	if (ret < 0) {
1322341618Scy		wpa_printf(MSG_ERROR,
1323341618Scy			   DRV_PREFIX "%s: failed to communicate: %d (%s)",
1324341618Scy			   __func__, ret, nl_geterror(-ret));
1325341618Scy	}
1326341618Scy
1327341618Scynla_put_failure:
1328341618Scy	nlmsg_free(msg);
1329341618Scy	return ret;
1330341618Scy}
1331341618Scy
1332341618Scy
1333341618Scy/**
1334341618Scy * macsec_drv_enable_transmit_sa - Enable SA for transmit
1335341618Scy * @priv: private driver interface data from init()
1336341618Scy * @sa: secure association
1337341618Scy * Returns: 0 on success, -1 on failure
1338341618Scy */
1339341618Scystatic int macsec_drv_enable_transmit_sa(void *priv, struct transmit_sa *sa)
1340341618Scy{
1341341618Scy	struct macsec_drv_data *drv = priv;
1342341618Scy	struct macsec_genl_ctx *ctx = &drv->ctx;
1343341618Scy	int ret;
1344341618Scy
1345346981Scy	wpa_printf(MSG_DEBUG, DRV_PREFIX "%s: enable_transmit_sa -> %d on "
1346346981Scy		   SCISTR, drv->ifname, sa->an,
1347346981Scy		   SCI2STR(sa->sc->sci.addr, sa->sc->sci.port));
1348341618Scy
1349341618Scy	ret = set_active_tx_sa(ctx, drv->ifi, sa->an, TRUE);
1350341618Scy	if (ret < 0) {
1351341618Scy		wpa_printf(MSG_ERROR, DRV_PREFIX "failed to enable txsa");
1352341618Scy		return ret;
1353341618Scy	}
1354341618Scy
1355341618Scy	drv->encoding_sa_set = TRUE;
1356341618Scy	drv->encoding_sa = sa->an;
1357341618Scy
1358341618Scy	return try_commit(drv);
1359341618Scy}
1360341618Scy
1361341618Scy
1362341618Scy/**
1363341618Scy * macsec_drv_disable_transmit_sa - Disable SA for transmit
1364341618Scy * @priv: private driver interface data from init()
1365341618Scy * @sa: secure association
1366341618Scy * Returns: 0 on success, -1 on failure
1367341618Scy */
1368341618Scystatic int macsec_drv_disable_transmit_sa(void *priv, struct transmit_sa *sa)
1369341618Scy{
1370341618Scy	struct macsec_drv_data *drv = priv;
1371341618Scy	struct macsec_genl_ctx *ctx = &drv->ctx;
1372341618Scy
1373346981Scy	wpa_printf(MSG_DEBUG, DRV_PREFIX "%s: disable_transmit_sa -> %d on "
1374346981Scy		   SCISTR, drv->ifname, sa->an,
1375346981Scy		   SCI2STR(sa->sc->sci.addr, sa->sc->sci.port));
1376341618Scy
1377341618Scy	return set_active_tx_sa(ctx, drv->ifi, sa->an, FALSE);
1378341618Scy}
1379341618Scy
1380341618Scy
1381346981Scystatic int macsec_drv_status(void *priv, char *buf, size_t buflen)
1382346981Scy{
1383346981Scy	struct macsec_drv_data *drv = priv;
1384346981Scy	int res;
1385346981Scy	char *pos, *end;
1386346981Scy
1387346981Scy	pos = buf;
1388346981Scy	end = buf + buflen;
1389346981Scy
1390346981Scy	res = os_snprintf(pos, end - pos,
1391346981Scy			  "ifname=%s\n"
1392346981Scy			  "ifi=%d\n"
1393346981Scy			  "parent_ifname=%s\n"
1394346981Scy			  "parent_ifi=%d\n",
1395346981Scy			  drv->common.ifname, drv->ifi,
1396346981Scy			  drv->ifname, drv->parent_ifi);
1397346981Scy	if (os_snprintf_error(end - pos, res))
1398346981Scy		return pos - buf;
1399346981Scy	pos += res;
1400346981Scy
1401346981Scy	return pos - buf;
1402346981Scy}
1403346981Scy
1404346981Scy
1405351611Scy#ifdef __linux__
1406351611Scy
1407351611Scystatic void macsec_drv_handle_data(void *ctx, unsigned char *buf, size_t len)
1408351611Scy{
1409351611Scy#ifdef HOSTAPD
1410351611Scy	struct ieee8023_hdr *hdr;
1411351611Scy	u8 *pos, *sa;
1412351611Scy	size_t left;
1413351611Scy	union wpa_event_data event;
1414351611Scy
1415351611Scy	/* must contain at least ieee8023_hdr 6 byte source, 6 byte dest,
1416351611Scy	 * 2 byte ethertype */
1417351611Scy	if (len < 14) {
1418351611Scy		wpa_printf(MSG_MSGDUMP, "%s: too short (%lu)",
1419351611Scy			   __func__, (unsigned long) len);
1420351611Scy		return;
1421351611Scy	}
1422351611Scy
1423351611Scy	hdr = (struct ieee8023_hdr *) buf;
1424351611Scy
1425351611Scy	switch (ntohs(hdr->ethertype)) {
1426351611Scy	case ETH_P_PAE:
1427351611Scy		wpa_printf(MSG_MSGDUMP, "Received EAPOL packet");
1428351611Scy		sa = hdr->src;
1429351611Scy		os_memset(&event, 0, sizeof(event));
1430351611Scy		event.new_sta.addr = sa;
1431351611Scy		wpa_supplicant_event(ctx, EVENT_NEW_STA, &event);
1432351611Scy
1433351611Scy		pos = (u8 *) (hdr + 1);
1434351611Scy		left = len - sizeof(*hdr);
1435351611Scy		drv_event_eapol_rx(ctx, sa, pos, left);
1436351611Scy		break;
1437351611Scy
1438351611Scy	default:
1439351611Scy		wpa_printf(MSG_DEBUG, "Unknown ethertype 0x%04x in data frame",
1440351611Scy			   ntohs(hdr->ethertype));
1441351611Scy		break;
1442351611Scy	}
1443351611Scy#endif /* HOSTAPD */
1444351611Scy}
1445351611Scy
1446351611Scy
1447351611Scystatic void macsec_drv_handle_read(int sock, void *eloop_ctx, void *sock_ctx)
1448351611Scy{
1449351611Scy	int len;
1450351611Scy	unsigned char buf[3000];
1451351611Scy
1452351611Scy	len = recv(sock, buf, sizeof(buf), 0);
1453351611Scy	if (len < 0) {
1454351611Scy		wpa_printf(MSG_ERROR, "macsec_linux: recv: %s",
1455351611Scy			   strerror(errno));
1456351611Scy		return;
1457351611Scy	}
1458351611Scy
1459351611Scy	macsec_drv_handle_data(eloop_ctx, buf, len);
1460351611Scy}
1461351611Scy
1462351611Scy#endif /* __linux__ */
1463351611Scy
1464351611Scy
1465351611Scystatic int macsec_drv_init_sockets(struct macsec_drv_data *drv, u8 *own_addr)
1466351611Scy{
1467351611Scy#ifdef __linux__
1468351611Scy	struct ifreq ifr;
1469351611Scy	struct sockaddr_ll addr;
1470351611Scy
1471351611Scy	drv->common.sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_PAE));
1472351611Scy	if (drv->common.sock < 0) {
1473351611Scy		wpa_printf(MSG_ERROR, "socket[PF_PACKET,SOCK_RAW]: %s",
1474351611Scy			   strerror(errno));
1475351611Scy		return -1;
1476351611Scy	}
1477351611Scy
1478351611Scy	if (eloop_register_read_sock(drv->common.sock, macsec_drv_handle_read,
1479351611Scy				     drv->common.ctx, NULL)) {
1480351611Scy		wpa_printf(MSG_INFO, "Could not register read socket");
1481351611Scy		return -1;
1482351611Scy	}
1483351611Scy
1484351611Scy	os_memset(&ifr, 0, sizeof(ifr));
1485351611Scy	os_strlcpy(ifr.ifr_name, drv->common.ifname, sizeof(ifr.ifr_name));
1486351611Scy	if (ioctl(drv->common.sock, SIOCGIFINDEX, &ifr) != 0) {
1487351611Scy		wpa_printf(MSG_ERROR, "ioctl(SIOCGIFINDEX): %s",
1488351611Scy			   strerror(errno));
1489351611Scy		return -1;
1490351611Scy	}
1491351611Scy
1492351611Scy	os_memset(&addr, 0, sizeof(addr));
1493351611Scy	addr.sll_family = AF_PACKET;
1494351611Scy	addr.sll_ifindex = ifr.ifr_ifindex;
1495351611Scy	wpa_printf(MSG_DEBUG, "Opening raw packet socket for ifindex %d",
1496351611Scy		   addr.sll_ifindex);
1497351611Scy
1498351611Scy	if (bind(drv->common.sock, (struct sockaddr *) &addr, sizeof(addr)) < 0)
1499351611Scy	{
1500351611Scy		wpa_printf(MSG_ERROR, "bind: %s", strerror(errno));
1501351611Scy		return -1;
1502351611Scy	}
1503351611Scy
1504351611Scy	/* filter multicast address */
1505351611Scy	if (wired_multicast_membership(drv->common.sock, ifr.ifr_ifindex,
1506351611Scy				       pae_group_addr, 1) < 0) {
1507351611Scy		wpa_printf(MSG_ERROR, "wired: Failed to add multicast group "
1508351611Scy			   "membership");
1509351611Scy		return -1;
1510351611Scy	}
1511351611Scy
1512351611Scy	os_memset(&ifr, 0, sizeof(ifr));
1513351611Scy	os_strlcpy(ifr.ifr_name, drv->common.ifname, sizeof(ifr.ifr_name));
1514351611Scy	if (ioctl(drv->common.sock, SIOCGIFHWADDR, &ifr) != 0) {
1515351611Scy		wpa_printf(MSG_ERROR, "ioctl(SIOCGIFHWADDR): %s",
1516351611Scy			   strerror(errno));
1517351611Scy		return -1;
1518351611Scy	}
1519351611Scy
1520351611Scy	if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) {
1521351611Scy		wpa_printf(MSG_INFO, "Invalid HW-addr family 0x%04x",
1522351611Scy			   ifr.ifr_hwaddr.sa_family);
1523351611Scy		return -1;
1524351611Scy	}
1525351611Scy	os_memcpy(own_addr, ifr.ifr_hwaddr.sa_data, ETH_ALEN);
1526351611Scy
1527351611Scy	return 0;
1528351611Scy#else /* __linux__ */
1529351611Scy	return -1;
1530351611Scy#endif /* __linux__ */
1531351611Scy}
1532351611Scy
1533351611Scy
1534351611Scystatic void * macsec_drv_hapd_init(struct hostapd_data *hapd,
1535351611Scy				   struct wpa_init_params *params)
1536351611Scy{
1537351611Scy	struct macsec_drv_data *drv;
1538351611Scy
1539351611Scy	drv = os_zalloc(sizeof(struct macsec_drv_data));
1540351611Scy	if (drv == NULL) {
1541351611Scy		wpa_printf(MSG_INFO,
1542351611Scy			   "Could not allocate memory for wired driver data");
1543351611Scy		return NULL;
1544351611Scy	}
1545351611Scy
1546351611Scy	drv->common.ctx = hapd;
1547351611Scy	os_strlcpy(drv->common.ifname, params->ifname,
1548351611Scy		   sizeof(drv->common.ifname));
1549351611Scy	drv->use_pae_group_addr = params->use_pae_group_addr;
1550351611Scy
1551351611Scy	if (macsec_drv_init_sockets(drv, params->own_addr)) {
1552351611Scy		os_free(drv);
1553351611Scy		return NULL;
1554351611Scy	}
1555351611Scy
1556351611Scy	return drv;
1557351611Scy}
1558351611Scy
1559351611Scy
1560351611Scystatic void macsec_drv_hapd_deinit(void *priv)
1561351611Scy{
1562351611Scy	struct macsec_drv_data *drv = priv;
1563351611Scy
1564351611Scy	if (drv->common.sock >= 0) {
1565351611Scy		eloop_unregister_read_sock(drv->common.sock);
1566351611Scy		close(drv->common.sock);
1567351611Scy	}
1568351611Scy
1569351611Scy	os_free(drv);
1570351611Scy}
1571351611Scy
1572351611Scy
1573351611Scystatic int macsec_drv_send_eapol(void *priv, const u8 *addr,
1574351611Scy				 const u8 *data, size_t data_len, int encrypt,
1575351611Scy				 const u8 *own_addr, u32 flags)
1576351611Scy{
1577351611Scy	struct macsec_drv_data *drv = priv;
1578351611Scy	struct ieee8023_hdr *hdr;
1579351611Scy	size_t len;
1580351611Scy	u8 *pos;
1581351611Scy	int res;
1582351611Scy
1583351611Scy	len = sizeof(*hdr) + data_len;
1584351611Scy	hdr = os_zalloc(len);
1585351611Scy	if (hdr == NULL) {
1586351611Scy		wpa_printf(MSG_INFO,
1587351611Scy			   "%s: malloc() failed (len=%lu)",
1588351611Scy			   __func__, (unsigned long) len);
1589351611Scy		return -1;
1590351611Scy	}
1591351611Scy
1592351611Scy	os_memcpy(hdr->dest, drv->use_pae_group_addr ? pae_group_addr : addr,
1593351611Scy		  ETH_ALEN);
1594351611Scy	os_memcpy(hdr->src, own_addr, ETH_ALEN);
1595351611Scy	hdr->ethertype = htons(ETH_P_PAE);
1596351611Scy
1597351611Scy	pos = (u8 *) (hdr + 1);
1598351611Scy	os_memcpy(pos, data, data_len);
1599351611Scy
1600351611Scy	res = send(drv->common.sock, (u8 *) hdr, len, 0);
1601351611Scy	os_free(hdr);
1602351611Scy
1603351611Scy	if (res < 0) {
1604351611Scy		wpa_printf(MSG_ERROR,
1605351611Scy			   "%s: packet len: %lu - failed: send: %s",
1606351611Scy			   __func__, (unsigned long) len, strerror(errno));
1607351611Scy	}
1608351611Scy
1609351611Scy	return res;
1610351611Scy}
1611351611Scy
1612351611Scy
1613341618Scyconst struct wpa_driver_ops wpa_driver_macsec_linux_ops = {
1614341618Scy	.name = "macsec_linux",
1615341618Scy	.desc = "MACsec Ethernet driver for Linux",
1616341618Scy	.get_ssid = driver_wired_get_ssid,
1617341618Scy	.get_bssid = driver_wired_get_bssid,
1618341618Scy	.get_capa = driver_wired_get_capa,
1619341618Scy	.init = macsec_drv_wpa_init,
1620341618Scy	.deinit = macsec_drv_wpa_deinit,
1621351611Scy	.hapd_init = macsec_drv_hapd_init,
1622351611Scy	.hapd_deinit = macsec_drv_hapd_deinit,
1623351611Scy	.hapd_send_eapol = macsec_drv_send_eapol,
1624341618Scy
1625341618Scy	.macsec_init = macsec_drv_macsec_init,
1626341618Scy	.macsec_deinit = macsec_drv_macsec_deinit,
1627341618Scy	.macsec_get_capability = macsec_drv_get_capability,
1628341618Scy	.enable_protect_frames = macsec_drv_enable_protect_frames,
1629341618Scy	.enable_encrypt = macsec_drv_enable_encrypt,
1630341618Scy	.set_replay_protect = macsec_drv_set_replay_protect,
1631341618Scy	.set_current_cipher_suite = macsec_drv_set_current_cipher_suite,
1632341618Scy	.enable_controlled_port = macsec_drv_enable_controlled_port,
1633341618Scy	.get_receive_lowest_pn = macsec_drv_get_receive_lowest_pn,
1634346981Scy	.set_receive_lowest_pn = macsec_drv_set_receive_lowest_pn,
1635341618Scy	.get_transmit_next_pn = macsec_drv_get_transmit_next_pn,
1636341618Scy	.set_transmit_next_pn = macsec_drv_set_transmit_next_pn,
1637341618Scy	.create_receive_sc = macsec_drv_create_receive_sc,
1638341618Scy	.delete_receive_sc = macsec_drv_delete_receive_sc,
1639341618Scy	.create_receive_sa = macsec_drv_create_receive_sa,
1640341618Scy	.delete_receive_sa = macsec_drv_delete_receive_sa,
1641341618Scy	.enable_receive_sa = macsec_drv_enable_receive_sa,
1642341618Scy	.disable_receive_sa = macsec_drv_disable_receive_sa,
1643341618Scy	.create_transmit_sc = macsec_drv_create_transmit_sc,
1644341618Scy	.delete_transmit_sc = macsec_drv_delete_transmit_sc,
1645341618Scy	.create_transmit_sa = macsec_drv_create_transmit_sa,
1646341618Scy	.delete_transmit_sa = macsec_drv_delete_transmit_sa,
1647341618Scy	.enable_transmit_sa = macsec_drv_enable_transmit_sa,
1648341618Scy	.disable_transmit_sa = macsec_drv_disable_transmit_sa,
1649346981Scy
1650346981Scy	.status = macsec_drv_status,
1651341618Scy};
1652