|
@@ -49,6 +49,7 @@
|
|
#include "core.h"
|
|
#include "core.h"
|
|
#include "bearer.h"
|
|
#include "bearer.h"
|
|
#include "netlink.h"
|
|
#include "netlink.h"
|
|
|
|
+#include "msg.h"
|
|
|
|
|
|
/* IANA assigned UDP port */
|
|
/* IANA assigned UDP port */
|
|
#define UDP_PORT_DEFAULT 6118
|
|
#define UDP_PORT_DEFAULT 6118
|
|
@@ -70,6 +71,13 @@ struct udp_media_addr {
|
|
};
|
|
};
|
|
};
|
|
};
|
|
|
|
|
|
|
|
+/* struct udp_replicast - container for UDP remote addresses */
|
|
|
|
+struct udp_replicast {
|
|
|
|
+ struct udp_media_addr addr;
|
|
|
|
+ struct rcu_head rcu;
|
|
|
|
+ struct list_head list;
|
|
|
|
+};
|
|
|
|
+
|
|
/**
|
|
/**
|
|
* struct udp_bearer - ip/udp bearer data structure
|
|
* struct udp_bearer - ip/udp bearer data structure
|
|
* @bearer: associated generic tipc bearer
|
|
* @bearer: associated generic tipc bearer
|
|
@@ -82,6 +90,7 @@ struct udp_bearer {
|
|
struct socket *ubsock;
|
|
struct socket *ubsock;
|
|
u32 ifindex;
|
|
u32 ifindex;
|
|
struct work_struct work;
|
|
struct work_struct work;
|
|
|
|
+ struct udp_replicast rcast;
|
|
};
|
|
};
|
|
|
|
|
|
static int tipc_udp_is_mcast_addr(struct udp_media_addr *addr)
|
|
static int tipc_udp_is_mcast_addr(struct udp_media_addr *addr)
|
|
@@ -203,29 +212,75 @@ static int tipc_udp_send_msg(struct net *net, struct sk_buff *skb,
|
|
{
|
|
{
|
|
struct udp_media_addr *src = (struct udp_media_addr *)&b->addr.value;
|
|
struct udp_media_addr *src = (struct udp_media_addr *)&b->addr.value;
|
|
struct udp_media_addr *dst = (struct udp_media_addr *)&addr->value;
|
|
struct udp_media_addr *dst = (struct udp_media_addr *)&addr->value;
|
|
|
|
+ struct udp_replicast *rcast;
|
|
struct udp_bearer *ub;
|
|
struct udp_bearer *ub;
|
|
int err = 0;
|
|
int err = 0;
|
|
|
|
|
|
if (skb_headroom(skb) < UDP_MIN_HEADROOM) {
|
|
if (skb_headroom(skb) < UDP_MIN_HEADROOM) {
|
|
err = pskb_expand_head(skb, UDP_MIN_HEADROOM, 0, GFP_ATOMIC);
|
|
err = pskb_expand_head(skb, UDP_MIN_HEADROOM, 0, GFP_ATOMIC);
|
|
if (err)
|
|
if (err)
|
|
- goto tx_error;
|
|
|
|
|
|
+ goto out;
|
|
}
|
|
}
|
|
|
|
|
|
skb_set_inner_protocol(skb, htons(ETH_P_TIPC));
|
|
skb_set_inner_protocol(skb, htons(ETH_P_TIPC));
|
|
ub = rcu_dereference_rtnl(b->media_ptr);
|
|
ub = rcu_dereference_rtnl(b->media_ptr);
|
|
if (!ub) {
|
|
if (!ub) {
|
|
err = -ENODEV;
|
|
err = -ENODEV;
|
|
- goto tx_error;
|
|
|
|
|
|
+ goto out;
|
|
}
|
|
}
|
|
|
|
|
|
- return tipc_udp_xmit(net, skb, ub, src, dst);
|
|
|
|
|
|
+ if (!addr->broadcast || list_empty(&ub->rcast.list))
|
|
|
|
+ return tipc_udp_xmit(net, skb, ub, src, dst);
|
|
|
|
|
|
-tx_error:
|
|
|
|
|
|
+ /* Replicast, send an skb to each configured IP address */
|
|
|
|
+ list_for_each_entry_rcu(rcast, &ub->rcast.list, list) {
|
|
|
|
+ struct sk_buff *_skb;
|
|
|
|
+
|
|
|
|
+ _skb = pskb_copy(skb, GFP_ATOMIC);
|
|
|
|
+ if (!_skb) {
|
|
|
|
+ err = -ENOMEM;
|
|
|
|
+ goto out;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ err = tipc_udp_xmit(net, _skb, ub, src, &rcast->addr);
|
|
|
|
+ if (err) {
|
|
|
|
+ kfree_skb(_skb);
|
|
|
|
+ goto out;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ err = 0;
|
|
|
|
+out:
|
|
kfree_skb(skb);
|
|
kfree_skb(skb);
|
|
return err;
|
|
return err;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static int tipc_udp_rcast_add(struct tipc_bearer *b,
|
|
|
|
+ struct udp_media_addr *addr)
|
|
|
|
+{
|
|
|
|
+ struct udp_replicast *rcast;
|
|
|
|
+ struct udp_bearer *ub;
|
|
|
|
+
|
|
|
|
+ ub = rcu_dereference_rtnl(b->media_ptr);
|
|
|
|
+ if (!ub)
|
|
|
|
+ return -ENODEV;
|
|
|
|
+
|
|
|
|
+ rcast = kmalloc(sizeof(*rcast), GFP_ATOMIC);
|
|
|
|
+ if (!rcast)
|
|
|
|
+ return -ENOMEM;
|
|
|
|
+
|
|
|
|
+ memcpy(&rcast->addr, addr, sizeof(struct udp_media_addr));
|
|
|
|
+
|
|
|
|
+ if (ntohs(addr->proto) == ETH_P_IP)
|
|
|
|
+ pr_info("New replicast peer: %pI4\n", &rcast->addr.ipv4);
|
|
|
|
+#if IS_ENABLED(CONFIG_IPV6)
|
|
|
|
+ else if (ntohs(addr->proto) == ETH_P_IPV6)
|
|
|
|
+ pr_info("New replicast peer: %pI6\n", &rcast->addr.ipv6);
|
|
|
|
+#endif
|
|
|
|
+
|
|
|
|
+ list_add_rcu(&rcast->list, &ub->rcast.list);
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
/* tipc_udp_recv - read data from bearer socket */
|
|
/* tipc_udp_recv - read data from bearer socket */
|
|
static int tipc_udp_recv(struct sock *sk, struct sk_buff *skb)
|
|
static int tipc_udp_recv(struct sock *sk, struct sk_buff *skb)
|
|
{
|
|
{
|
|
@@ -320,6 +375,32 @@ static int tipc_parse_udp_addr(struct nlattr *nla, struct udp_media_addr *addr,
|
|
return -EADDRNOTAVAIL;
|
|
return -EADDRNOTAVAIL;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+int tipc_udp_nl_bearer_add(struct tipc_bearer *b, struct nlattr *attr)
|
|
|
|
+{
|
|
|
|
+ int err;
|
|
|
|
+ struct udp_media_addr addr = {0};
|
|
|
|
+ struct nlattr *opts[TIPC_NLA_UDP_MAX + 1];
|
|
|
|
+ struct udp_media_addr *dst;
|
|
|
|
+
|
|
|
|
+ if (nla_parse_nested(opts, TIPC_NLA_UDP_MAX, attr, tipc_nl_udp_policy))
|
|
|
|
+ return -EINVAL;
|
|
|
|
+
|
|
|
|
+ if (!opts[TIPC_NLA_UDP_REMOTE])
|
|
|
|
+ return -EINVAL;
|
|
|
|
+
|
|
|
|
+ err = tipc_parse_udp_addr(opts[TIPC_NLA_UDP_REMOTE], &addr, NULL);
|
|
|
|
+ if (err)
|
|
|
|
+ return err;
|
|
|
|
+
|
|
|
|
+ dst = (struct udp_media_addr *)&b->bcast_addr.value;
|
|
|
|
+ if (tipc_udp_is_mcast_addr(dst)) {
|
|
|
|
+ pr_err("Can't add remote ip to TIPC UDP multicast bearer\n");
|
|
|
|
+ return -EINVAL;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return tipc_udp_rcast_add(b, &addr);
|
|
|
|
+}
|
|
|
|
+
|
|
/**
|
|
/**
|
|
* tipc_udp_enable - callback to create a new udp bearer instance
|
|
* tipc_udp_enable - callback to create a new udp bearer instance
|
|
* @net: network namespace
|
|
* @net: network namespace
|
|
@@ -334,7 +415,7 @@ static int tipc_udp_enable(struct net *net, struct tipc_bearer *b,
|
|
{
|
|
{
|
|
int err = -EINVAL;
|
|
int err = -EINVAL;
|
|
struct udp_bearer *ub;
|
|
struct udp_bearer *ub;
|
|
- struct udp_media_addr *remote;
|
|
|
|
|
|
+ struct udp_media_addr remote = {0};
|
|
struct udp_media_addr local = {0};
|
|
struct udp_media_addr local = {0};
|
|
struct udp_port_cfg udp_conf = {0};
|
|
struct udp_port_cfg udp_conf = {0};
|
|
struct udp_tunnel_sock_cfg tuncfg = {NULL};
|
|
struct udp_tunnel_sock_cfg tuncfg = {NULL};
|
|
@@ -344,6 +425,8 @@ static int tipc_udp_enable(struct net *net, struct tipc_bearer *b,
|
|
if (!ub)
|
|
if (!ub)
|
|
return -ENOMEM;
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
+ INIT_LIST_HEAD(&ub->rcast.list);
|
|
|
|
+
|
|
if (!attrs[TIPC_NLA_BEARER_UDP_OPTS])
|
|
if (!attrs[TIPC_NLA_BEARER_UDP_OPTS])
|
|
goto err;
|
|
goto err;
|
|
|
|
|
|
@@ -362,9 +445,7 @@ static int tipc_udp_enable(struct net *net, struct tipc_bearer *b,
|
|
if (err)
|
|
if (err)
|
|
goto err;
|
|
goto err;
|
|
|
|
|
|
- remote = (struct udp_media_addr *)&b->bcast_addr.value;
|
|
|
|
- memset(remote, 0, sizeof(struct udp_media_addr));
|
|
|
|
- err = tipc_parse_udp_addr(opts[TIPC_NLA_UDP_REMOTE], remote, NULL);
|
|
|
|
|
|
+ err = tipc_parse_udp_addr(opts[TIPC_NLA_UDP_REMOTE], &remote, NULL);
|
|
if (err)
|
|
if (err)
|
|
goto err;
|
|
goto err;
|
|
|
|
|
|
@@ -409,10 +490,17 @@ static int tipc_udp_enable(struct net *net, struct tipc_bearer *b,
|
|
tuncfg.encap_destroy = NULL;
|
|
tuncfg.encap_destroy = NULL;
|
|
setup_udp_tunnel_sock(net, ub->ubsock, &tuncfg);
|
|
setup_udp_tunnel_sock(net, ub->ubsock, &tuncfg);
|
|
|
|
|
|
- if (tipc_udp_is_mcast_addr(remote)) {
|
|
|
|
- if (enable_mcast(ub, remote))
|
|
|
|
- goto err;
|
|
|
|
- }
|
|
|
|
|
|
+ /**
|
|
|
|
+ * The bcast media address port is used for all peers and the ip
|
|
|
|
+ * is used if it's a multicast address.
|
|
|
|
+ */
|
|
|
|
+ memcpy(&b->bcast_addr.value, &remote, sizeof(remote));
|
|
|
|
+ if (tipc_udp_is_mcast_addr(&remote))
|
|
|
|
+ err = enable_mcast(ub, &remote);
|
|
|
|
+ else
|
|
|
|
+ err = tipc_udp_rcast_add(b, &remote);
|
|
|
|
+ if (err)
|
|
|
|
+ goto err;
|
|
|
|
|
|
return 0;
|
|
return 0;
|
|
err:
|
|
err:
|
|
@@ -424,6 +512,12 @@ err:
|
|
static void cleanup_bearer(struct work_struct *work)
|
|
static void cleanup_bearer(struct work_struct *work)
|
|
{
|
|
{
|
|
struct udp_bearer *ub = container_of(work, struct udp_bearer, work);
|
|
struct udp_bearer *ub = container_of(work, struct udp_bearer, work);
|
|
|
|
+ struct udp_replicast *rcast, *tmp;
|
|
|
|
+
|
|
|
|
+ list_for_each_entry_safe(rcast, tmp, &ub->rcast.list, list) {
|
|
|
|
+ list_del_rcu(&rcast->list);
|
|
|
|
+ kfree_rcu(rcast, rcu);
|
|
|
|
+ }
|
|
|
|
|
|
if (ub->ubsock)
|
|
if (ub->ubsock)
|
|
udp_tunnel_sock_release(ub->ubsock);
|
|
udp_tunnel_sock_release(ub->ubsock);
|