l2tp : multicast notification to the registered listeners

Previously l2tp module did not provide any means for the user space to
get notified when tunnels/sessions are added/modified/deleted.
This change contains the following
- create a multicast group for the listeners to register.
- notify the registered listeners when the tunnels/sessions are
  created/modified/deleted.

Signed-off-by: Bill Hong <bhong@brocade.com>
Reviewed-by: Stephen Hemminger <stephen@networkplumber.org>
Reviewed-by: Sven-Thorsten Dietrich <sven@brocade.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Bill Hong 2014-12-27 10:12:39 -08:00 committed by David S. Miller
parent 957f094f22
commit 33f72e6f0c
2 changed files with 92 additions and 10 deletions

View File

@ -178,5 +178,6 @@ enum l2tp_seqmode {
*/ */
#define L2TP_GENL_NAME "l2tp" #define L2TP_GENL_NAME "l2tp"
#define L2TP_GENL_VERSION 0x1 #define L2TP_GENL_VERSION 0x1
#define L2TP_GENL_MCGROUP "l2tp"
#endif /* _UAPI_LINUX_L2TP_H_ */ #endif /* _UAPI_LINUX_L2TP_H_ */

View File

@ -40,6 +40,18 @@ static struct genl_family l2tp_nl_family = {
.netnsok = true, .netnsok = true,
}; };
static const struct genl_multicast_group l2tp_multicast_group[] = {
{
.name = L2TP_GENL_MCGROUP,
},
};
static int l2tp_nl_tunnel_send(struct sk_buff *skb, u32 portid, u32 seq,
int flags, struct l2tp_tunnel *tunnel, u8 cmd);
static int l2tp_nl_session_send(struct sk_buff *skb, u32 portid, u32 seq,
int flags, struct l2tp_session *session,
u8 cmd);
/* Accessed under genl lock */ /* Accessed under genl lock */
static const struct l2tp_nl_cmd_ops *l2tp_nl_cmd_ops[__L2TP_PWTYPE_MAX]; static const struct l2tp_nl_cmd_ops *l2tp_nl_cmd_ops[__L2TP_PWTYPE_MAX];
@ -97,6 +109,52 @@ static int l2tp_nl_cmd_noop(struct sk_buff *skb, struct genl_info *info)
return ret; return ret;
} }
static int l2tp_tunnel_notify(struct genl_family *family,
struct genl_info *info,
struct l2tp_tunnel *tunnel,
u8 cmd)
{
struct sk_buff *msg;
int ret;
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
if (!msg)
return -ENOMEM;
ret = l2tp_nl_tunnel_send(msg, info->snd_portid, info->snd_seq,
NLM_F_ACK, tunnel, cmd);
if (ret >= 0)
return genlmsg_multicast_allns(family, msg, 0, 0, GFP_ATOMIC);
nlmsg_free(msg);
return ret;
}
static int l2tp_session_notify(struct genl_family *family,
struct genl_info *info,
struct l2tp_session *session,
u8 cmd)
{
struct sk_buff *msg;
int ret;
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
if (!msg)
return -ENOMEM;
ret = l2tp_nl_session_send(msg, info->snd_portid, info->snd_seq,
NLM_F_ACK, session, cmd);
if (ret >= 0)
return genlmsg_multicast_allns(family, msg, 0, 0, GFP_ATOMIC);
nlmsg_free(msg);
return ret;
}
static int l2tp_nl_cmd_tunnel_create(struct sk_buff *skb, struct genl_info *info) static int l2tp_nl_cmd_tunnel_create(struct sk_buff *skb, struct genl_info *info)
{ {
u32 tunnel_id; u32 tunnel_id;
@ -188,6 +246,9 @@ static int l2tp_nl_cmd_tunnel_create(struct sk_buff *skb, struct genl_info *info
break; break;
} }
if (ret >= 0)
ret = l2tp_tunnel_notify(&l2tp_nl_family, info,
tunnel, L2TP_CMD_TUNNEL_CREATE);
out: out:
return ret; return ret;
} }
@ -211,6 +272,9 @@ static int l2tp_nl_cmd_tunnel_delete(struct sk_buff *skb, struct genl_info *info
goto out; goto out;
} }
l2tp_tunnel_notify(&l2tp_nl_family, info,
tunnel, L2TP_CMD_TUNNEL_DELETE);
(void) l2tp_tunnel_delete(tunnel); (void) l2tp_tunnel_delete(tunnel);
out: out:
@ -239,12 +303,15 @@ static int l2tp_nl_cmd_tunnel_modify(struct sk_buff *skb, struct genl_info *info
if (info->attrs[L2TP_ATTR_DEBUG]) if (info->attrs[L2TP_ATTR_DEBUG])
tunnel->debug = nla_get_u32(info->attrs[L2TP_ATTR_DEBUG]); tunnel->debug = nla_get_u32(info->attrs[L2TP_ATTR_DEBUG]);
ret = l2tp_tunnel_notify(&l2tp_nl_family, info,
tunnel, L2TP_CMD_TUNNEL_MODIFY);
out: out:
return ret; return ret;
} }
static int l2tp_nl_tunnel_send(struct sk_buff *skb, u32 portid, u32 seq, int flags, static int l2tp_nl_tunnel_send(struct sk_buff *skb, u32 portid, u32 seq, int flags,
struct l2tp_tunnel *tunnel) struct l2tp_tunnel *tunnel, u8 cmd)
{ {
void *hdr; void *hdr;
struct nlattr *nest; struct nlattr *nest;
@ -254,8 +321,7 @@ static int l2tp_nl_tunnel_send(struct sk_buff *skb, u32 portid, u32 seq, int fla
struct ipv6_pinfo *np = NULL; struct ipv6_pinfo *np = NULL;
#endif #endif
hdr = genlmsg_put(skb, portid, seq, &l2tp_nl_family, flags, hdr = genlmsg_put(skb, portid, seq, &l2tp_nl_family, flags, cmd);
L2TP_CMD_TUNNEL_GET);
if (!hdr) if (!hdr)
return -EMSGSIZE; return -EMSGSIZE;
@ -359,7 +425,7 @@ static int l2tp_nl_cmd_tunnel_get(struct sk_buff *skb, struct genl_info *info)
} }
ret = l2tp_nl_tunnel_send(msg, info->snd_portid, info->snd_seq, ret = l2tp_nl_tunnel_send(msg, info->snd_portid, info->snd_seq,
NLM_F_ACK, tunnel); NLM_F_ACK, tunnel, L2TP_CMD_TUNNEL_GET);
if (ret < 0) if (ret < 0)
goto err_out; goto err_out;
@ -385,7 +451,7 @@ static int l2tp_nl_cmd_tunnel_dump(struct sk_buff *skb, struct netlink_callback
if (l2tp_nl_tunnel_send(skb, NETLINK_CB(cb->skb).portid, if (l2tp_nl_tunnel_send(skb, NETLINK_CB(cb->skb).portid,
cb->nlh->nlmsg_seq, NLM_F_MULTI, cb->nlh->nlmsg_seq, NLM_F_MULTI,
tunnel) <= 0) tunnel, L2TP_CMD_TUNNEL_GET) <= 0)
goto out; goto out;
ti++; ti++;
@ -539,6 +605,13 @@ static int l2tp_nl_cmd_session_create(struct sk_buff *skb, struct genl_info *inf
ret = (*l2tp_nl_cmd_ops[cfg.pw_type]->session_create)(net, tunnel_id, ret = (*l2tp_nl_cmd_ops[cfg.pw_type]->session_create)(net, tunnel_id,
session_id, peer_session_id, &cfg); session_id, peer_session_id, &cfg);
if (ret >= 0) {
session = l2tp_session_find(net, tunnel, session_id);
if (session)
ret = l2tp_session_notify(&l2tp_nl_family, info, session,
L2TP_CMD_SESSION_CREATE);
}
out: out:
return ret; return ret;
} }
@ -555,6 +628,9 @@ static int l2tp_nl_cmd_session_delete(struct sk_buff *skb, struct genl_info *inf
goto out; goto out;
} }
l2tp_session_notify(&l2tp_nl_family, info,
session, L2TP_CMD_SESSION_DELETE);
pw_type = session->pwtype; pw_type = session->pwtype;
if (pw_type < __L2TP_PWTYPE_MAX) if (pw_type < __L2TP_PWTYPE_MAX)
if (l2tp_nl_cmd_ops[pw_type] && l2tp_nl_cmd_ops[pw_type]->session_delete) if (l2tp_nl_cmd_ops[pw_type] && l2tp_nl_cmd_ops[pw_type]->session_delete)
@ -601,12 +677,15 @@ static int l2tp_nl_cmd_session_modify(struct sk_buff *skb, struct genl_info *inf
if (info->attrs[L2TP_ATTR_MRU]) if (info->attrs[L2TP_ATTR_MRU])
session->mru = nla_get_u16(info->attrs[L2TP_ATTR_MRU]); session->mru = nla_get_u16(info->attrs[L2TP_ATTR_MRU]);
ret = l2tp_session_notify(&l2tp_nl_family, info,
session, L2TP_CMD_SESSION_MODIFY);
out: out:
return ret; return ret;
} }
static int l2tp_nl_session_send(struct sk_buff *skb, u32 portid, u32 seq, int flags, static int l2tp_nl_session_send(struct sk_buff *skb, u32 portid, u32 seq, int flags,
struct l2tp_session *session) struct l2tp_session *session, u8 cmd)
{ {
void *hdr; void *hdr;
struct nlattr *nest; struct nlattr *nest;
@ -615,7 +694,7 @@ static int l2tp_nl_session_send(struct sk_buff *skb, u32 portid, u32 seq, int fl
sk = tunnel->sock; sk = tunnel->sock;
hdr = genlmsg_put(skb, portid, seq, &l2tp_nl_family, flags, L2TP_CMD_SESSION_GET); hdr = genlmsg_put(skb, portid, seq, &l2tp_nl_family, flags, cmd);
if (!hdr) if (!hdr)
return -EMSGSIZE; return -EMSGSIZE;
@ -699,7 +778,7 @@ static int l2tp_nl_cmd_session_get(struct sk_buff *skb, struct genl_info *info)
} }
ret = l2tp_nl_session_send(msg, info->snd_portid, info->snd_seq, ret = l2tp_nl_session_send(msg, info->snd_portid, info->snd_seq,
0, session); 0, session, L2TP_CMD_SESSION_GET);
if (ret < 0) if (ret < 0)
goto err_out; goto err_out;
@ -737,7 +816,7 @@ static int l2tp_nl_cmd_session_dump(struct sk_buff *skb, struct netlink_callback
if (l2tp_nl_session_send(skb, NETLINK_CB(cb->skb).portid, if (l2tp_nl_session_send(skb, NETLINK_CB(cb->skb).portid,
cb->nlh->nlmsg_seq, NLM_F_MULTI, cb->nlh->nlmsg_seq, NLM_F_MULTI,
session) <= 0) session, L2TP_CMD_SESSION_GET) <= 0)
break; break;
si++; si++;
@ -896,7 +975,9 @@ EXPORT_SYMBOL_GPL(l2tp_nl_unregister_ops);
static int l2tp_nl_init(void) static int l2tp_nl_init(void)
{ {
pr_info("L2TP netlink interface\n"); pr_info("L2TP netlink interface\n");
return genl_register_family_with_ops(&l2tp_nl_family, l2tp_nl_ops); return genl_register_family_with_ops_groups(&l2tp_nl_family,
l2tp_nl_ops,
l2tp_multicast_group);
} }
static void l2tp_nl_cleanup(void) static void l2tp_nl_cleanup(void)