mirror of
https://github.com/torvalds/linux.git
synced 2026-05-30 01:53:29 +02:00
Merge branch 'net-mctp-add-support-for-gateway-routing'
Jeremy Kerr says:
====================
net: mctp: Add support for gateway routing
This series adds a gateway route type for the MCTP core, allowing
non-local EIDs as the match for a route.
Example setup using the mctp tools:
mctp route add 9 via mctpi2c0
mctp neigh add 9 dev mctpi2c0 lladdr 0x1d
mctp route add 10 gw 9
- will route packets to eid 10 through mctpi2c0, using a dest lladdr
of 0x1d (ie, that of the directly-attached eid 9).
The core change to support this is the introduction of a struct
mctp_dst, which represents the result of a route lookup. Since this
involves a bit of surgery through the routing code, we add a few tests
along the way.
We're introducing an ABI change in the new RTM_{NEW,GET,DEL}ROUTE
netlink formats, with the support for a RTA_GATEWAY attribute. Because
we need a network ID specified to fully-qualify a gateway EID, the
RTA_GATEWAY attribute carries the (net, eid) tuple in full:
struct mctp_fq_addr {
unsigned int net;
mctp_eid_t eid;
}
Of course, any questions, comments etc are most welcome.
Signed-off-by: Jeremy Kerr <jk@codeconstruct.com.au>
====================
Link: https://patch.msgid.link/20250702-dev-forwarding-v5-0-1468191da8a4@codeconstruct.com.au
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
This commit is contained in:
commit
d23647fd54
|
|
@ -183,8 +183,8 @@ struct mctp_sk_key {
|
|||
struct mctp_skb_cb {
|
||||
unsigned int magic;
|
||||
unsigned int net;
|
||||
int ifindex; /* extended/direct addressing if set */
|
||||
mctp_eid_t src;
|
||||
/* fields below provide extended addressing for ingress to recvmsg() */
|
||||
int ifindex;
|
||||
unsigned char halen;
|
||||
unsigned char haddr[MAX_ADDR_LEN];
|
||||
};
|
||||
|
|
@ -222,6 +222,8 @@ struct mctp_flow {
|
|||
struct mctp_sk_key *key;
|
||||
};
|
||||
|
||||
struct mctp_dst;
|
||||
|
||||
/* Route definition.
|
||||
*
|
||||
* These are held in the pernet->mctp.routes list, with RCU protection for
|
||||
|
|
@ -229,16 +231,25 @@ struct mctp_flow {
|
|||
* dropped on NETDEV_UNREGISTER events.
|
||||
*
|
||||
* Updates to the route table are performed under rtnl; all reads under RCU,
|
||||
* so routes cannot be referenced over a RCU grace period. Specifically: A
|
||||
* caller cannot block between mctp_route_lookup and mctp_route_release()
|
||||
* so routes cannot be referenced over a RCU grace period.
|
||||
*/
|
||||
struct mctp_route {
|
||||
mctp_eid_t min, max;
|
||||
|
||||
unsigned char type;
|
||||
|
||||
unsigned int mtu;
|
||||
struct mctp_dev *dev;
|
||||
int (*output)(struct mctp_route *route,
|
||||
|
||||
enum {
|
||||
MCTP_ROUTE_DIRECT,
|
||||
MCTP_ROUTE_GATEWAY,
|
||||
} dst_type;
|
||||
union {
|
||||
struct mctp_dev *dev;
|
||||
struct mctp_fq_addr gateway;
|
||||
};
|
||||
|
||||
int (*output)(struct mctp_dst *dst,
|
||||
struct sk_buff *skb);
|
||||
|
||||
struct list_head list;
|
||||
|
|
@ -246,12 +257,35 @@ struct mctp_route {
|
|||
struct rcu_head rcu;
|
||||
};
|
||||
|
||||
/* Route lookup result: dst. Represents the results of a routing decision,
|
||||
* but is only held over the individual routing operation.
|
||||
*
|
||||
* Will typically be stored on the caller stack, and must be released after
|
||||
* usage.
|
||||
*/
|
||||
struct mctp_dst {
|
||||
struct mctp_dev *dev;
|
||||
unsigned int mtu;
|
||||
mctp_eid_t nexthop;
|
||||
|
||||
/* set for direct addressing */
|
||||
unsigned char halen;
|
||||
unsigned char haddr[MAX_ADDR_LEN];
|
||||
|
||||
int (*output)(struct mctp_dst *dst, struct sk_buff *skb);
|
||||
};
|
||||
|
||||
int mctp_dst_from_extaddr(struct mctp_dst *dst, struct net *net, int ifindex,
|
||||
unsigned char halen, const unsigned char *haddr);
|
||||
|
||||
/* route interfaces */
|
||||
struct mctp_route *mctp_route_lookup(struct net *net, unsigned int dnet,
|
||||
mctp_eid_t daddr);
|
||||
int mctp_route_lookup(struct net *net, unsigned int dnet,
|
||||
mctp_eid_t daddr, struct mctp_dst *dst);
|
||||
|
||||
void mctp_dst_release(struct mctp_dst *dst);
|
||||
|
||||
/* always takes ownership of skb */
|
||||
int mctp_local_output(struct sock *sk, struct mctp_route *rt,
|
||||
int mctp_local_output(struct sock *sk, struct mctp_dst *dst,
|
||||
struct sk_buff *skb, mctp_eid_t daddr, u8 req_tag);
|
||||
|
||||
void mctp_key_unref(struct mctp_sk_key *key);
|
||||
|
|
|
|||
|
|
@ -37,6 +37,14 @@ struct sockaddr_mctp_ext {
|
|||
__u8 smctp_haddr[MAX_ADDR_LEN];
|
||||
};
|
||||
|
||||
/* A "fully qualified" MCTP address, which includes the system-local network ID,
|
||||
* required to uniquely resolve a routable EID.
|
||||
*/
|
||||
struct mctp_fq_addr {
|
||||
unsigned int net;
|
||||
mctp_eid_t eid;
|
||||
};
|
||||
|
||||
#define MCTP_NET_ANY 0x0
|
||||
|
||||
#define MCTP_ADDR_NULL 0x00
|
||||
|
|
|
|||
|
|
@ -97,8 +97,8 @@ static int mctp_sendmsg(struct socket *sock, struct msghdr *msg, size_t len)
|
|||
struct sock *sk = sock->sk;
|
||||
struct mctp_sock *msk = container_of(sk, struct mctp_sock, sk);
|
||||
struct mctp_skb_cb *cb;
|
||||
struct mctp_route *rt;
|
||||
struct sk_buff *skb = NULL;
|
||||
struct mctp_dst dst;
|
||||
int hlen;
|
||||
|
||||
if (addr) {
|
||||
|
|
@ -133,34 +133,30 @@ static int mctp_sendmsg(struct socket *sock, struct msghdr *msg, size_t len)
|
|||
if (msk->addr_ext && addrlen >= sizeof(struct sockaddr_mctp_ext)) {
|
||||
DECLARE_SOCKADDR(struct sockaddr_mctp_ext *,
|
||||
extaddr, msg->msg_name);
|
||||
struct net_device *dev;
|
||||
|
||||
rc = -EINVAL;
|
||||
rcu_read_lock();
|
||||
dev = dev_get_by_index_rcu(sock_net(sk), extaddr->smctp_ifindex);
|
||||
/* check for correct halen */
|
||||
if (dev && extaddr->smctp_halen == dev->addr_len) {
|
||||
hlen = LL_RESERVED_SPACE(dev) + sizeof(struct mctp_hdr);
|
||||
rc = 0;
|
||||
}
|
||||
rcu_read_unlock();
|
||||
if (!mctp_sockaddr_ext_is_ok(extaddr))
|
||||
return -EINVAL;
|
||||
|
||||
rc = mctp_dst_from_extaddr(&dst, sock_net(sk),
|
||||
extaddr->smctp_ifindex,
|
||||
extaddr->smctp_halen,
|
||||
extaddr->smctp_haddr);
|
||||
if (rc)
|
||||
goto err_free;
|
||||
rt = NULL;
|
||||
return rc;
|
||||
|
||||
} else {
|
||||
rt = mctp_route_lookup(sock_net(sk), addr->smctp_network,
|
||||
addr->smctp_addr.s_addr);
|
||||
if (!rt) {
|
||||
rc = -EHOSTUNREACH;
|
||||
goto err_free;
|
||||
}
|
||||
hlen = LL_RESERVED_SPACE(rt->dev->dev) + sizeof(struct mctp_hdr);
|
||||
rc = mctp_route_lookup(sock_net(sk), addr->smctp_network,
|
||||
addr->smctp_addr.s_addr, &dst);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
hlen = LL_RESERVED_SPACE(dst.dev->dev) + sizeof(struct mctp_hdr);
|
||||
|
||||
skb = sock_alloc_send_skb(sk, hlen + 1 + len,
|
||||
msg->msg_flags & MSG_DONTWAIT, &rc);
|
||||
if (!skb)
|
||||
return rc;
|
||||
goto err_release_dst;
|
||||
|
||||
skb_reserve(skb, hlen);
|
||||
|
||||
|
|
@ -175,30 +171,16 @@ static int mctp_sendmsg(struct socket *sock, struct msghdr *msg, size_t len)
|
|||
cb = __mctp_cb(skb);
|
||||
cb->net = addr->smctp_network;
|
||||
|
||||
if (!rt) {
|
||||
/* fill extended address in cb */
|
||||
DECLARE_SOCKADDR(struct sockaddr_mctp_ext *,
|
||||
extaddr, msg->msg_name);
|
||||
|
||||
if (!mctp_sockaddr_ext_is_ok(extaddr) ||
|
||||
extaddr->smctp_halen > sizeof(cb->haddr)) {
|
||||
rc = -EINVAL;
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
cb->ifindex = extaddr->smctp_ifindex;
|
||||
/* smctp_halen is checked above */
|
||||
cb->halen = extaddr->smctp_halen;
|
||||
memcpy(cb->haddr, extaddr->smctp_haddr, cb->halen);
|
||||
}
|
||||
|
||||
rc = mctp_local_output(sk, rt, skb, addr->smctp_addr.s_addr,
|
||||
rc = mctp_local_output(sk, &dst, skb, addr->smctp_addr.s_addr,
|
||||
addr->smctp_tag);
|
||||
|
||||
mctp_dst_release(&dst);
|
||||
return rc ? : len;
|
||||
|
||||
err_free:
|
||||
kfree_skb(skb);
|
||||
err_release_dst:
|
||||
mctp_dst_release(&dst);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
|
@ -793,3 +775,7 @@ MODULE_DESCRIPTION("MCTP core");
|
|||
MODULE_AUTHOR("Jeremy Kerr <jk@codeconstruct.com.au>");
|
||||
|
||||
MODULE_ALIAS_NETPROTO(PF_MCTP);
|
||||
|
||||
#if IS_ENABLED(CONFIG_MCTP_TEST)
|
||||
#include "test/sock-test.c"
|
||||
#endif
|
||||
|
|
|
|||
590
net/mctp/route.c
590
net/mctp/route.c
|
|
@ -17,6 +17,8 @@
|
|||
#include <linux/rtnetlink.h>
|
||||
#include <linux/skbuff.h>
|
||||
|
||||
#include <kunit/static_stub.h>
|
||||
|
||||
#include <uapi/linux/if_arp.h>
|
||||
|
||||
#include <net/mctp.h>
|
||||
|
|
@ -32,7 +34,7 @@ static const unsigned long mctp_key_lifetime = 6 * CONFIG_HZ;
|
|||
static void mctp_flow_prepare_output(struct sk_buff *skb, struct mctp_dev *dev);
|
||||
|
||||
/* route output callbacks */
|
||||
static int mctp_route_discard(struct mctp_route *route, struct sk_buff *skb)
|
||||
static int mctp_dst_discard(struct mctp_dst *dst, struct sk_buff *skb)
|
||||
{
|
||||
kfree_skb(skb);
|
||||
return 0;
|
||||
|
|
@ -368,7 +370,7 @@ static int mctp_frag_queue(struct mctp_sk_key *key, struct sk_buff *skb)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int mctp_route_input(struct mctp_route *route, struct sk_buff *skb)
|
||||
static int mctp_dst_input(struct mctp_dst *dst, struct sk_buff *skb)
|
||||
{
|
||||
struct mctp_sk_key *key, *any_key = NULL;
|
||||
struct net *net = dev_net(skb->dev);
|
||||
|
|
@ -392,6 +394,9 @@ static int mctp_route_input(struct mctp_route *route, struct sk_buff *skb)
|
|||
*/
|
||||
skb_orphan(skb);
|
||||
|
||||
if (skb->pkt_type == PACKET_OUTGOING)
|
||||
skb->pkt_type = PACKET_LOOPBACK;
|
||||
|
||||
/* ensure we have enough data for a header and a type */
|
||||
if (skb->len < sizeof(struct mctp_hdr) + 1)
|
||||
goto out;
|
||||
|
|
@ -556,39 +561,31 @@ static int mctp_route_input(struct mctp_route *route, struct sk_buff *skb)
|
|||
return rc;
|
||||
}
|
||||
|
||||
static unsigned int mctp_route_mtu(struct mctp_route *rt)
|
||||
static int mctp_dst_output(struct mctp_dst *dst, struct sk_buff *skb)
|
||||
{
|
||||
return rt->mtu ?: READ_ONCE(rt->dev->dev->mtu);
|
||||
}
|
||||
|
||||
static int mctp_route_output(struct mctp_route *route, struct sk_buff *skb)
|
||||
{
|
||||
struct mctp_skb_cb *cb = mctp_cb(skb);
|
||||
struct mctp_hdr *hdr = mctp_hdr(skb);
|
||||
char daddr_buf[MAX_ADDR_LEN];
|
||||
char *daddr = NULL;
|
||||
unsigned int mtu;
|
||||
int rc;
|
||||
|
||||
skb->protocol = htons(ETH_P_MCTP);
|
||||
skb->pkt_type = PACKET_OUTGOING;
|
||||
|
||||
mtu = READ_ONCE(skb->dev->mtu);
|
||||
if (skb->len > mtu) {
|
||||
if (skb->len > dst->mtu) {
|
||||
kfree_skb(skb);
|
||||
return -EMSGSIZE;
|
||||
}
|
||||
|
||||
if (cb->ifindex) {
|
||||
/* direct route; use the hwaddr we stashed in sendmsg */
|
||||
if (cb->halen != skb->dev->addr_len) {
|
||||
/* direct route; use the hwaddr we stashed in sendmsg */
|
||||
if (dst->halen) {
|
||||
if (dst->halen != skb->dev->addr_len) {
|
||||
/* sanity check, sendmsg should have already caught this */
|
||||
kfree_skb(skb);
|
||||
return -EMSGSIZE;
|
||||
}
|
||||
daddr = cb->haddr;
|
||||
daddr = dst->haddr;
|
||||
} else {
|
||||
/* If lookup fails let the device handle daddr==NULL */
|
||||
if (mctp_neigh_lookup(route->dev, hdr->dest, daddr_buf) == 0)
|
||||
if (mctp_neigh_lookup(dst->dev, dst->nexthop, daddr_buf) == 0)
|
||||
daddr = daddr_buf;
|
||||
}
|
||||
|
||||
|
|
@ -599,7 +596,7 @@ static int mctp_route_output(struct mctp_route *route, struct sk_buff *skb)
|
|||
return -EHOSTUNREACH;
|
||||
}
|
||||
|
||||
mctp_flow_prepare_output(skb, route->dev);
|
||||
mctp_flow_prepare_output(skb, dst->dev);
|
||||
|
||||
rc = dev_queue_xmit(skb);
|
||||
if (rc)
|
||||
|
|
@ -612,7 +609,8 @@ static int mctp_route_output(struct mctp_route *route, struct sk_buff *skb)
|
|||
static void mctp_route_release(struct mctp_route *rt)
|
||||
{
|
||||
if (refcount_dec_and_test(&rt->refs)) {
|
||||
mctp_dev_put(rt->dev);
|
||||
if (rt->dst_type == MCTP_ROUTE_DIRECT)
|
||||
mctp_dev_put(rt->dev);
|
||||
kfree_rcu(rt, rcu);
|
||||
}
|
||||
}
|
||||
|
|
@ -628,7 +626,7 @@ static struct mctp_route *mctp_route_alloc(void)
|
|||
|
||||
INIT_LIST_HEAD(&rt->list);
|
||||
refcount_set(&rt->refs, 1);
|
||||
rt->output = mctp_route_discard;
|
||||
rt->output = mctp_dst_discard;
|
||||
|
||||
return rt;
|
||||
}
|
||||
|
|
@ -801,10 +799,16 @@ static struct mctp_sk_key *mctp_lookup_prealloc_tag(struct mctp_sock *msk,
|
|||
}
|
||||
|
||||
/* routing lookups */
|
||||
static unsigned int mctp_route_netid(struct mctp_route *rt)
|
||||
{
|
||||
return rt->dst_type == MCTP_ROUTE_DIRECT ?
|
||||
READ_ONCE(rt->dev->net) : rt->gateway.net;
|
||||
}
|
||||
|
||||
static bool mctp_rt_match_eid(struct mctp_route *rt,
|
||||
unsigned int net, mctp_eid_t eid)
|
||||
{
|
||||
return READ_ONCE(rt->dev->net) == net &&
|
||||
return mctp_route_netid(rt) == net &&
|
||||
rt->min <= eid && rt->max >= eid;
|
||||
}
|
||||
|
||||
|
|
@ -813,54 +817,150 @@ static bool mctp_rt_compare_exact(struct mctp_route *rt1,
|
|||
struct mctp_route *rt2)
|
||||
{
|
||||
ASSERT_RTNL();
|
||||
return rt1->dev->net == rt2->dev->net &&
|
||||
return mctp_route_netid(rt1) == mctp_route_netid(rt2) &&
|
||||
rt1->min == rt2->min &&
|
||||
rt1->max == rt2->max;
|
||||
}
|
||||
|
||||
struct mctp_route *mctp_route_lookup(struct net *net, unsigned int dnet,
|
||||
mctp_eid_t daddr)
|
||||
/* must only be called on a direct route, as the final output hop */
|
||||
static void mctp_dst_from_route(struct mctp_dst *dst, mctp_eid_t eid,
|
||||
unsigned int mtu, struct mctp_route *route)
|
||||
{
|
||||
struct mctp_route *tmp, *rt = NULL;
|
||||
|
||||
rcu_read_lock();
|
||||
|
||||
list_for_each_entry_rcu(tmp, &net->mctp.routes, list) {
|
||||
/* TODO: add metrics */
|
||||
if (mctp_rt_match_eid(tmp, dnet, daddr)) {
|
||||
if (refcount_inc_not_zero(&tmp->refs)) {
|
||||
rt = tmp;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rcu_read_unlock();
|
||||
|
||||
return rt;
|
||||
mctp_dev_hold(route->dev);
|
||||
dst->nexthop = eid;
|
||||
dst->dev = route->dev;
|
||||
dst->mtu = READ_ONCE(dst->dev->dev->mtu);
|
||||
if (mtu)
|
||||
dst->mtu = min(dst->mtu, mtu);
|
||||
dst->halen = 0;
|
||||
dst->output = route->output;
|
||||
}
|
||||
|
||||
static struct mctp_route *mctp_route_lookup_null(struct net *net,
|
||||
struct net_device *dev)
|
||||
int mctp_dst_from_extaddr(struct mctp_dst *dst, struct net *net, int ifindex,
|
||||
unsigned char halen, const unsigned char *haddr)
|
||||
{
|
||||
struct mctp_route *tmp, *rt = NULL;
|
||||
struct net_device *netdev;
|
||||
struct mctp_dev *dev;
|
||||
int rc = -ENOENT;
|
||||
|
||||
if (halen > sizeof(dst->haddr))
|
||||
return -EINVAL;
|
||||
|
||||
rcu_read_lock();
|
||||
|
||||
list_for_each_entry_rcu(tmp, &net->mctp.routes, list) {
|
||||
if (tmp->dev->dev == dev && tmp->type == RTN_LOCAL &&
|
||||
refcount_inc_not_zero(&tmp->refs)) {
|
||||
rt = tmp;
|
||||
netdev = dev_get_by_index_rcu(net, ifindex);
|
||||
if (!netdev)
|
||||
goto out_unlock;
|
||||
|
||||
if (netdev->addr_len != halen) {
|
||||
rc = -EINVAL;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
dev = __mctp_dev_get(netdev);
|
||||
if (!dev)
|
||||
goto out_unlock;
|
||||
|
||||
dst->dev = dev;
|
||||
dst->mtu = READ_ONCE(netdev->mtu);
|
||||
dst->halen = halen;
|
||||
dst->output = mctp_dst_output;
|
||||
dst->nexthop = 0;
|
||||
memcpy(dst->haddr, haddr, halen);
|
||||
|
||||
rc = 0;
|
||||
|
||||
out_unlock:
|
||||
rcu_read_unlock();
|
||||
return rc;
|
||||
}
|
||||
|
||||
void mctp_dst_release(struct mctp_dst *dst)
|
||||
{
|
||||
mctp_dev_put(dst->dev);
|
||||
}
|
||||
|
||||
static struct mctp_route *mctp_route_lookup_single(struct net *net,
|
||||
unsigned int dnet,
|
||||
mctp_eid_t daddr)
|
||||
{
|
||||
struct mctp_route *rt;
|
||||
|
||||
list_for_each_entry_rcu(rt, &net->mctp.routes, list) {
|
||||
if (mctp_rt_match_eid(rt, dnet, daddr))
|
||||
return rt;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* populates *dst on successful lookup, if set */
|
||||
int mctp_route_lookup(struct net *net, unsigned int dnet,
|
||||
mctp_eid_t daddr, struct mctp_dst *dst)
|
||||
{
|
||||
const unsigned int max_depth = 32;
|
||||
unsigned int depth, mtu = 0;
|
||||
int rc = -EHOSTUNREACH;
|
||||
|
||||
rcu_read_lock();
|
||||
|
||||
for (depth = 0; depth < max_depth; depth++) {
|
||||
struct mctp_route *rt;
|
||||
|
||||
rt = mctp_route_lookup_single(net, dnet, daddr);
|
||||
if (!rt)
|
||||
break;
|
||||
|
||||
/* clamp mtu to the smallest in the path, allowing 0
|
||||
* to specify no restrictions
|
||||
*/
|
||||
if (mtu && rt->mtu)
|
||||
mtu = min(mtu, rt->mtu);
|
||||
else
|
||||
mtu = mtu ?: rt->mtu;
|
||||
|
||||
if (rt->dst_type == MCTP_ROUTE_DIRECT) {
|
||||
if (dst)
|
||||
mctp_dst_from_route(dst, daddr, mtu, rt);
|
||||
rc = 0;
|
||||
break;
|
||||
|
||||
} else if (rt->dst_type == MCTP_ROUTE_GATEWAY) {
|
||||
daddr = rt->gateway.eid;
|
||||
}
|
||||
}
|
||||
|
||||
rcu_read_unlock();
|
||||
|
||||
return rt;
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int mctp_do_fragment_route(struct mctp_route *rt, struct sk_buff *skb,
|
||||
static int mctp_route_lookup_null(struct net *net, struct net_device *dev,
|
||||
struct mctp_dst *dst)
|
||||
{
|
||||
int rc = -EHOSTUNREACH;
|
||||
struct mctp_route *rt;
|
||||
|
||||
rcu_read_lock();
|
||||
|
||||
list_for_each_entry_rcu(rt, &net->mctp.routes, list) {
|
||||
if (rt->dst_type != MCTP_ROUTE_DIRECT || rt->type != RTN_LOCAL)
|
||||
continue;
|
||||
|
||||
if (rt->dev->dev != dev)
|
||||
continue;
|
||||
|
||||
mctp_dst_from_route(dst, 0, 0, rt);
|
||||
rc = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
rcu_read_unlock();
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int mctp_do_fragment_route(struct mctp_dst *dst, struct sk_buff *skb,
|
||||
unsigned int mtu, u8 tag)
|
||||
{
|
||||
const unsigned int hlen = sizeof(struct mctp_hdr);
|
||||
|
|
@ -933,7 +1033,7 @@ static int mctp_do_fragment_route(struct mctp_route *rt, struct sk_buff *skb,
|
|||
skb_ext_copy(skb2, skb);
|
||||
|
||||
/* do route */
|
||||
rc = rt->output(rt, skb2);
|
||||
rc = dst->output(dst, skb2);
|
||||
if (rc)
|
||||
break;
|
||||
|
||||
|
|
@ -945,68 +1045,34 @@ static int mctp_do_fragment_route(struct mctp_route *rt, struct sk_buff *skb,
|
|||
return rc;
|
||||
}
|
||||
|
||||
int mctp_local_output(struct sock *sk, struct mctp_route *rt,
|
||||
int mctp_local_output(struct sock *sk, struct mctp_dst *dst,
|
||||
struct sk_buff *skb, mctp_eid_t daddr, u8 req_tag)
|
||||
{
|
||||
struct mctp_sock *msk = container_of(sk, struct mctp_sock, sk);
|
||||
struct mctp_skb_cb *cb = mctp_cb(skb);
|
||||
struct mctp_route tmp_rt = {0};
|
||||
struct mctp_sk_key *key;
|
||||
struct mctp_hdr *hdr;
|
||||
unsigned long flags;
|
||||
unsigned int netid;
|
||||
unsigned int mtu;
|
||||
mctp_eid_t saddr;
|
||||
bool ext_rt;
|
||||
int rc;
|
||||
u8 tag;
|
||||
|
||||
KUNIT_STATIC_STUB_REDIRECT(mctp_local_output, sk, dst, skb, daddr,
|
||||
req_tag);
|
||||
|
||||
rc = -ENODEV;
|
||||
|
||||
if (rt) {
|
||||
ext_rt = false;
|
||||
if (WARN_ON(!rt->dev))
|
||||
goto out_release;
|
||||
|
||||
} else if (cb->ifindex) {
|
||||
struct net_device *dev;
|
||||
|
||||
ext_rt = true;
|
||||
rt = &tmp_rt;
|
||||
|
||||
rcu_read_lock();
|
||||
dev = dev_get_by_index_rcu(sock_net(sk), cb->ifindex);
|
||||
if (!dev) {
|
||||
rcu_read_unlock();
|
||||
goto out_free;
|
||||
}
|
||||
rt->dev = __mctp_dev_get(dev);
|
||||
rcu_read_unlock();
|
||||
|
||||
if (!rt->dev)
|
||||
goto out_release;
|
||||
|
||||
/* establish temporary route - we set up enough to keep
|
||||
* mctp_route_output happy
|
||||
*/
|
||||
rt->output = mctp_route_output;
|
||||
rt->mtu = 0;
|
||||
|
||||
} else {
|
||||
rc = -EINVAL;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&rt->dev->addrs_lock, flags);
|
||||
if (rt->dev->num_addrs == 0) {
|
||||
spin_lock_irqsave(&dst->dev->addrs_lock, flags);
|
||||
if (dst->dev->num_addrs == 0) {
|
||||
rc = -EHOSTUNREACH;
|
||||
} else {
|
||||
/* use the outbound interface's first address as our source */
|
||||
saddr = rt->dev->addrs[0];
|
||||
saddr = dst->dev->addrs[0];
|
||||
rc = 0;
|
||||
}
|
||||
spin_unlock_irqrestore(&rt->dev->addrs_lock, flags);
|
||||
netid = READ_ONCE(rt->dev->net);
|
||||
spin_unlock_irqrestore(&dst->dev->addrs_lock, flags);
|
||||
netid = READ_ONCE(dst->dev->net);
|
||||
|
||||
if (rc)
|
||||
goto out_release;
|
||||
|
|
@ -1032,15 +1098,13 @@ int mctp_local_output(struct sock *sk, struct mctp_route *rt,
|
|||
tag = req_tag & MCTP_TAG_MASK;
|
||||
}
|
||||
|
||||
skb->pkt_type = PACKET_OUTGOING;
|
||||
skb->protocol = htons(ETH_P_MCTP);
|
||||
skb->priority = 0;
|
||||
skb_reset_transport_header(skb);
|
||||
skb_push(skb, sizeof(struct mctp_hdr));
|
||||
skb_reset_network_header(skb);
|
||||
skb->dev = rt->dev->dev;
|
||||
|
||||
/* cb->net will have been set on initial ingress */
|
||||
cb->src = saddr;
|
||||
skb->dev = dst->dev->dev;
|
||||
|
||||
/* set up common header fields */
|
||||
hdr = mctp_hdr(skb);
|
||||
|
|
@ -1048,73 +1112,64 @@ int mctp_local_output(struct sock *sk, struct mctp_route *rt,
|
|||
hdr->dest = daddr;
|
||||
hdr->src = saddr;
|
||||
|
||||
mtu = mctp_route_mtu(rt);
|
||||
mtu = dst->mtu;
|
||||
|
||||
if (skb->len + sizeof(struct mctp_hdr) <= mtu) {
|
||||
hdr->flags_seq_tag = MCTP_HDR_FLAG_SOM |
|
||||
MCTP_HDR_FLAG_EOM | tag;
|
||||
rc = rt->output(rt, skb);
|
||||
rc = dst->output(dst, skb);
|
||||
} else {
|
||||
rc = mctp_do_fragment_route(rt, skb, mtu, tag);
|
||||
rc = mctp_do_fragment_route(dst, skb, mtu, tag);
|
||||
}
|
||||
|
||||
/* route output functions consume the skb, even on error */
|
||||
skb = NULL;
|
||||
|
||||
out_release:
|
||||
if (!ext_rt)
|
||||
mctp_route_release(rt);
|
||||
|
||||
mctp_dev_put(tmp_rt.dev);
|
||||
|
||||
out_free:
|
||||
kfree_skb(skb);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* route management */
|
||||
static int mctp_route_add(struct mctp_dev *mdev, mctp_eid_t daddr_start,
|
||||
unsigned int daddr_extent, unsigned int mtu,
|
||||
unsigned char type)
|
||||
|
||||
/* mctp_route_add(): Add the provided route, previously allocated via
|
||||
* mctp_route_alloc(). On success, takes ownership of @rt, which includes a
|
||||
* hold on rt->dev for usage in the route table. On failure a caller will want
|
||||
* to mctp_route_release().
|
||||
*
|
||||
* We expect that the caller has set rt->type, rt->dst_type, rt->min, rt->max,
|
||||
* rt->mtu and either rt->dev (with a reference held appropriately) or
|
||||
* rt->gateway. Other fields will be populated.
|
||||
*/
|
||||
static int mctp_route_add(struct net *net, struct mctp_route *rt)
|
||||
{
|
||||
int (*rtfn)(struct mctp_route *rt, struct sk_buff *skb);
|
||||
struct net *net = dev_net(mdev->dev);
|
||||
struct mctp_route *rt, *ert;
|
||||
struct mctp_route *ert;
|
||||
|
||||
if (!mctp_address_unicast(daddr_start))
|
||||
if (!mctp_address_unicast(rt->min) || !mctp_address_unicast(rt->max))
|
||||
return -EINVAL;
|
||||
|
||||
if (daddr_extent > 0xff || daddr_start + daddr_extent >= 255)
|
||||
if (rt->dst_type == MCTP_ROUTE_DIRECT && !rt->dev)
|
||||
return -EINVAL;
|
||||
|
||||
switch (type) {
|
||||
if (rt->dst_type == MCTP_ROUTE_GATEWAY && !rt->gateway.eid)
|
||||
return -EINVAL;
|
||||
|
||||
switch (rt->type) {
|
||||
case RTN_LOCAL:
|
||||
rtfn = mctp_route_input;
|
||||
rt->output = mctp_dst_input;
|
||||
break;
|
||||
case RTN_UNICAST:
|
||||
rtfn = mctp_route_output;
|
||||
rt->output = mctp_dst_output;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
rt = mctp_route_alloc();
|
||||
if (!rt)
|
||||
return -ENOMEM;
|
||||
|
||||
rt->min = daddr_start;
|
||||
rt->max = daddr_start + daddr_extent;
|
||||
rt->mtu = mtu;
|
||||
rt->dev = mdev;
|
||||
mctp_dev_hold(rt->dev);
|
||||
rt->type = type;
|
||||
rt->output = rtfn;
|
||||
|
||||
ASSERT_RTNL();
|
||||
|
||||
/* Prevent duplicate identical routes. */
|
||||
list_for_each_entry(ert, &net->mctp.routes, list) {
|
||||
if (mctp_rt_compare_exact(rt, ert)) {
|
||||
mctp_route_release(rt);
|
||||
return -EEXIST;
|
||||
}
|
||||
}
|
||||
|
|
@ -1124,10 +1179,10 @@ static int mctp_route_add(struct mctp_dev *mdev, mctp_eid_t daddr_start,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int mctp_route_remove(struct mctp_dev *mdev, mctp_eid_t daddr_start,
|
||||
unsigned int daddr_extent, unsigned char type)
|
||||
static int mctp_route_remove(struct net *net, unsigned int netid,
|
||||
mctp_eid_t daddr_start, unsigned int daddr_extent,
|
||||
unsigned char type)
|
||||
{
|
||||
struct net *net = dev_net(mdev->dev);
|
||||
struct mctp_route *rt, *tmp;
|
||||
mctp_eid_t daddr_end;
|
||||
bool dropped;
|
||||
|
|
@ -1141,7 +1196,7 @@ static int mctp_route_remove(struct mctp_dev *mdev, mctp_eid_t daddr_start,
|
|||
ASSERT_RTNL();
|
||||
|
||||
list_for_each_entry_safe(rt, tmp, &net->mctp.routes, list) {
|
||||
if (rt->dev == mdev &&
|
||||
if (mctp_route_netid(rt) == netid &&
|
||||
rt->min == daddr_start && rt->max == daddr_end &&
|
||||
rt->type == type) {
|
||||
list_del_rcu(&rt->list);
|
||||
|
|
@ -1156,12 +1211,32 @@ static int mctp_route_remove(struct mctp_dev *mdev, mctp_eid_t daddr_start,
|
|||
|
||||
int mctp_route_add_local(struct mctp_dev *mdev, mctp_eid_t addr)
|
||||
{
|
||||
return mctp_route_add(mdev, addr, 0, 0, RTN_LOCAL);
|
||||
struct mctp_route *rt;
|
||||
int rc;
|
||||
|
||||
rt = mctp_route_alloc();
|
||||
if (!rt)
|
||||
return -ENOMEM;
|
||||
|
||||
rt->min = addr;
|
||||
rt->max = addr;
|
||||
rt->dst_type = MCTP_ROUTE_DIRECT;
|
||||
rt->dev = mdev;
|
||||
rt->type = RTN_LOCAL;
|
||||
|
||||
mctp_dev_hold(rt->dev);
|
||||
|
||||
rc = mctp_route_add(dev_net(mdev->dev), rt);
|
||||
if (rc)
|
||||
mctp_route_release(rt);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int mctp_route_remove_local(struct mctp_dev *mdev, mctp_eid_t addr)
|
||||
{
|
||||
return mctp_route_remove(mdev, addr, 0, RTN_LOCAL);
|
||||
return mctp_route_remove(dev_net(mdev->dev), mdev->net,
|
||||
addr, 0, RTN_LOCAL);
|
||||
}
|
||||
|
||||
/* removes all entries for a given device */
|
||||
|
|
@ -1172,7 +1247,7 @@ void mctp_route_remove_dev(struct mctp_dev *mdev)
|
|||
|
||||
ASSERT_RTNL();
|
||||
list_for_each_entry_safe(rt, tmp, &net->mctp.routes, list) {
|
||||
if (rt->dev == mdev) {
|
||||
if (rt->dst_type == MCTP_ROUTE_DIRECT && rt->dev == mdev) {
|
||||
list_del_rcu(&rt->list);
|
||||
/* TODO: immediate RTM_DELROUTE */
|
||||
mctp_route_release(rt);
|
||||
|
|
@ -1189,8 +1264,9 @@ static int mctp_pkttype_receive(struct sk_buff *skb, struct net_device *dev,
|
|||
struct net *net = dev_net(dev);
|
||||
struct mctp_dev *mdev;
|
||||
struct mctp_skb_cb *cb;
|
||||
struct mctp_route *rt;
|
||||
struct mctp_dst dst;
|
||||
struct mctp_hdr *mh;
|
||||
int rc;
|
||||
|
||||
rcu_read_lock();
|
||||
mdev = __mctp_dev_get(dev);
|
||||
|
|
@ -1232,17 +1308,17 @@ static int mctp_pkttype_receive(struct sk_buff *skb, struct net_device *dev,
|
|||
cb->net = READ_ONCE(mdev->net);
|
||||
cb->ifindex = dev->ifindex;
|
||||
|
||||
rt = mctp_route_lookup(net, cb->net, mh->dest);
|
||||
rc = mctp_route_lookup(net, cb->net, mh->dest, &dst);
|
||||
|
||||
/* NULL EID, but addressed to our physical address */
|
||||
if (!rt && mh->dest == MCTP_ADDR_NULL && skb->pkt_type == PACKET_HOST)
|
||||
rt = mctp_route_lookup_null(net, dev);
|
||||
if (rc && mh->dest == MCTP_ADDR_NULL && skb->pkt_type == PACKET_HOST)
|
||||
rc = mctp_route_lookup_null(net, dev, &dst);
|
||||
|
||||
if (!rt)
|
||||
if (rc)
|
||||
goto err_drop;
|
||||
|
||||
rt->output(rt, skb);
|
||||
mctp_route_release(rt);
|
||||
dst.output(&dst, skb);
|
||||
mctp_dst_release(&dst);
|
||||
mctp_dev_put(mdev);
|
||||
|
||||
return NET_RX_SUCCESS;
|
||||
|
|
@ -1264,19 +1340,28 @@ static const struct nla_policy rta_mctp_policy[RTA_MAX + 1] = {
|
|||
[RTA_DST] = { .type = NLA_U8 },
|
||||
[RTA_METRICS] = { .type = NLA_NESTED },
|
||||
[RTA_OIF] = { .type = NLA_U32 },
|
||||
[RTA_GATEWAY] = NLA_POLICY_EXACT_LEN(sizeof(struct mctp_fq_addr)),
|
||||
};
|
||||
|
||||
/* Common part for RTM_NEWROUTE and RTM_DELROUTE parsing.
|
||||
* tb must hold RTA_MAX+1 elements.
|
||||
static const struct nla_policy rta_metrics_policy[RTAX_MAX + 1] = {
|
||||
[RTAX_MTU] = { .type = NLA_U32 },
|
||||
};
|
||||
|
||||
/* base parsing; common to both _lookup and _populate variants.
|
||||
*
|
||||
* For gateway routes (which have a RTA_GATEWAY, and no RTA_OIF), we populate
|
||||
* *gatweayp. for direct routes (RTA_OIF, no RTA_GATEWAY), we populate *mdev.
|
||||
*/
|
||||
static int mctp_route_nlparse(struct sk_buff *skb, struct nlmsghdr *nlh,
|
||||
struct netlink_ext_ack *extack,
|
||||
struct nlattr **tb, struct rtmsg **rtm,
|
||||
struct mctp_dev **mdev, mctp_eid_t *daddr_start)
|
||||
static int mctp_route_nlparse_common(struct net *net, struct nlmsghdr *nlh,
|
||||
struct netlink_ext_ack *extack,
|
||||
struct nlattr **tb, struct rtmsg **rtm,
|
||||
struct mctp_dev **mdev,
|
||||
struct mctp_fq_addr *gatewayp,
|
||||
mctp_eid_t *daddr_start)
|
||||
{
|
||||
struct net *net = sock_net(skb->sk);
|
||||
struct mctp_fq_addr *gateway = NULL;
|
||||
unsigned int ifindex = 0;
|
||||
struct net_device *dev;
|
||||
unsigned int ifindex;
|
||||
int rc;
|
||||
|
||||
rc = nlmsg_parse(nlh, sizeof(struct rtmsg), tb, RTA_MAX,
|
||||
|
|
@ -1292,11 +1377,44 @@ static int mctp_route_nlparse(struct sk_buff *skb, struct nlmsghdr *nlh,
|
|||
}
|
||||
*daddr_start = nla_get_u8(tb[RTA_DST]);
|
||||
|
||||
if (!tb[RTA_OIF]) {
|
||||
NL_SET_ERR_MSG(extack, "ifindex missing");
|
||||
if (tb[RTA_OIF])
|
||||
ifindex = nla_get_u32(tb[RTA_OIF]);
|
||||
|
||||
if (tb[RTA_GATEWAY])
|
||||
gateway = nla_data(tb[RTA_GATEWAY]);
|
||||
|
||||
if (ifindex && gateway) {
|
||||
NL_SET_ERR_MSG(extack,
|
||||
"cannot specify both ifindex and gateway");
|
||||
return -EINVAL;
|
||||
|
||||
} else if (ifindex) {
|
||||
dev = __dev_get_by_index(net, ifindex);
|
||||
if (!dev) {
|
||||
NL_SET_ERR_MSG(extack, "bad ifindex");
|
||||
return -ENODEV;
|
||||
}
|
||||
*mdev = mctp_dev_get_rtnl(dev);
|
||||
if (!*mdev)
|
||||
return -ENODEV;
|
||||
gatewayp->eid = 0;
|
||||
|
||||
} else if (gateway) {
|
||||
if (!mctp_address_unicast(gateway->eid)) {
|
||||
NL_SET_ERR_MSG(extack, "bad gateway");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
gatewayp->eid = gateway->eid;
|
||||
gatewayp->net = gateway->net != MCTP_NET_ANY ?
|
||||
gateway->net :
|
||||
READ_ONCE(net->mctp.default_net);
|
||||
*mdev = NULL;
|
||||
|
||||
} else {
|
||||
NL_SET_ERR_MSG(extack, "no route output provided");
|
||||
return -EINVAL;
|
||||
}
|
||||
ifindex = nla_get_u32(tb[RTA_OIF]);
|
||||
|
||||
*rtm = nlmsg_data(nlh);
|
||||
if ((*rtm)->rtm_family != AF_MCTP) {
|
||||
|
|
@ -1304,82 +1422,157 @@ static int mctp_route_nlparse(struct sk_buff *skb, struct nlmsghdr *nlh,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
dev = __dev_get_by_index(net, ifindex);
|
||||
if (!dev) {
|
||||
NL_SET_ERR_MSG(extack, "bad ifindex");
|
||||
return -ENODEV;
|
||||
}
|
||||
*mdev = mctp_dev_get_rtnl(dev);
|
||||
if (!*mdev)
|
||||
return -ENODEV;
|
||||
|
||||
if (dev->flags & IFF_LOOPBACK) {
|
||||
NL_SET_ERR_MSG(extack, "no routes to loopback");
|
||||
if ((*rtm)->rtm_type != RTN_UNICAST) {
|
||||
NL_SET_ERR_MSG(extack, "rtm_type must be RTN_UNICAST");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct nla_policy rta_metrics_policy[RTAX_MAX + 1] = {
|
||||
[RTAX_MTU] = { .type = NLA_U32 },
|
||||
};
|
||||
|
||||
static int mctp_newroute(struct sk_buff *skb, struct nlmsghdr *nlh,
|
||||
struct netlink_ext_ack *extack)
|
||||
/* Route parsing for lookup operations; we only need the "route target"
|
||||
* components (ie., network and dest-EID range).
|
||||
*/
|
||||
static int mctp_route_nlparse_lookup(struct net *net, struct nlmsghdr *nlh,
|
||||
struct netlink_ext_ack *extack,
|
||||
unsigned char *type, unsigned int *netid,
|
||||
mctp_eid_t *daddr_start,
|
||||
unsigned int *daddr_extent)
|
||||
{
|
||||
struct nlattr *tb[RTA_MAX + 1];
|
||||
struct nlattr *tbx[RTAX_MAX + 1];
|
||||
mctp_eid_t daddr_start;
|
||||
struct mctp_fq_addr gw;
|
||||
struct mctp_dev *mdev;
|
||||
struct rtmsg *rtm;
|
||||
unsigned int mtu;
|
||||
int rc;
|
||||
|
||||
rc = mctp_route_nlparse(skb, nlh, extack, tb,
|
||||
&rtm, &mdev, &daddr_start);
|
||||
if (rc < 0)
|
||||
rc = mctp_route_nlparse_common(net, nlh, extack, tb, &rtm,
|
||||
&mdev, &gw, daddr_start);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
if (rtm->rtm_type != RTN_UNICAST) {
|
||||
NL_SET_ERR_MSG(extack, "rtm_type must be RTN_UNICAST");
|
||||
if (mdev) {
|
||||
*netid = mdev->net;
|
||||
} else if (gw.eid) {
|
||||
*netid = gw.net;
|
||||
} else {
|
||||
/* bug: _nlparse_common should not allow this */
|
||||
return -1;
|
||||
}
|
||||
|
||||
*type = rtm->rtm_type;
|
||||
*daddr_extent = rtm->rtm_dst_len;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Full route parse for RTM_NEWROUTE: populate @rt. On success,
|
||||
* MCTP_ROUTE_DIRECT routes (ie, those with a direct dev) will hold a reference
|
||||
* to that dev.
|
||||
*/
|
||||
static int mctp_route_nlparse_populate(struct net *net, struct nlmsghdr *nlh,
|
||||
struct netlink_ext_ack *extack,
|
||||
struct mctp_route *rt)
|
||||
{
|
||||
struct nlattr *tbx[RTAX_MAX + 1];
|
||||
struct nlattr *tb[RTA_MAX + 1];
|
||||
unsigned int daddr_extent;
|
||||
struct mctp_fq_addr gw;
|
||||
mctp_eid_t daddr_start;
|
||||
struct mctp_dev *dev;
|
||||
struct rtmsg *rtm;
|
||||
u32 mtu = 0;
|
||||
int rc;
|
||||
|
||||
rc = mctp_route_nlparse_common(net, nlh, extack, tb, &rtm,
|
||||
&dev, &gw, &daddr_start);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
daddr_extent = rtm->rtm_dst_len;
|
||||
|
||||
if (daddr_extent > 0xff || daddr_extent + daddr_start >= 255) {
|
||||
NL_SET_ERR_MSG(extack, "invalid eid range");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mtu = 0;
|
||||
if (tb[RTA_METRICS]) {
|
||||
rc = nla_parse_nested(tbx, RTAX_MAX, tb[RTA_METRICS],
|
||||
rta_metrics_policy, NULL);
|
||||
if (rc < 0)
|
||||
if (rc < 0) {
|
||||
NL_SET_ERR_MSG(extack, "incorrect RTA_METRICS format");
|
||||
return rc;
|
||||
}
|
||||
if (tbx[RTAX_MTU])
|
||||
mtu = nla_get_u32(tbx[RTAX_MTU]);
|
||||
}
|
||||
|
||||
rc = mctp_route_add(mdev, daddr_start, rtm->rtm_dst_len, mtu,
|
||||
rtm->rtm_type);
|
||||
rt->type = rtm->rtm_type;
|
||||
rt->min = daddr_start;
|
||||
rt->max = daddr_start + daddr_extent;
|
||||
rt->mtu = mtu;
|
||||
if (gw.eid) {
|
||||
rt->dst_type = MCTP_ROUTE_GATEWAY;
|
||||
rt->gateway.eid = gw.eid;
|
||||
rt->gateway.net = gw.net;
|
||||
} else {
|
||||
rt->dst_type = MCTP_ROUTE_DIRECT;
|
||||
rt->dev = dev;
|
||||
mctp_dev_hold(rt->dev);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mctp_newroute(struct sk_buff *skb, struct nlmsghdr *nlh,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
struct net *net = sock_net(skb->sk);
|
||||
struct mctp_route *rt;
|
||||
int rc;
|
||||
|
||||
rt = mctp_route_alloc();
|
||||
if (!rt)
|
||||
return -ENOMEM;
|
||||
|
||||
rc = mctp_route_nlparse_populate(net, nlh, extack, rt);
|
||||
if (rc < 0)
|
||||
goto err_free;
|
||||
|
||||
if (rt->dst_type == MCTP_ROUTE_DIRECT &&
|
||||
rt->dev->dev->flags & IFF_LOOPBACK) {
|
||||
NL_SET_ERR_MSG(extack, "no routes to loopback");
|
||||
rc = -EINVAL;
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
rc = mctp_route_add(net, rt);
|
||||
if (!rc)
|
||||
return 0;
|
||||
|
||||
err_free:
|
||||
mctp_route_release(rt);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int mctp_delroute(struct sk_buff *skb, struct nlmsghdr *nlh,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
struct nlattr *tb[RTA_MAX + 1];
|
||||
struct net *net = sock_net(skb->sk);
|
||||
unsigned int netid, daddr_extent;
|
||||
unsigned char type = RTN_UNSPEC;
|
||||
mctp_eid_t daddr_start;
|
||||
struct mctp_dev *mdev;
|
||||
struct rtmsg *rtm;
|
||||
int rc;
|
||||
|
||||
rc = mctp_route_nlparse(skb, nlh, extack, tb,
|
||||
&rtm, &mdev, &daddr_start);
|
||||
rc = mctp_route_nlparse_lookup(net, nlh, extack, &type, &netid,
|
||||
&daddr_start, &daddr_extent);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
/* we only have unicast routes */
|
||||
if (rtm->rtm_type != RTN_UNICAST)
|
||||
if (type != RTN_UNICAST)
|
||||
return -EINVAL;
|
||||
|
||||
rc = mctp_route_remove(mdev, daddr_start, rtm->rtm_dst_len, RTN_UNICAST);
|
||||
rc = mctp_route_remove(net, netid, daddr_start, daddr_extent, type);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
|
@ -1405,7 +1598,6 @@ static int mctp_fill_rtinfo(struct sk_buff *skb, struct mctp_route *rt,
|
|||
hdr->rtm_tos = 0;
|
||||
hdr->rtm_table = RT_TABLE_DEFAULT;
|
||||
hdr->rtm_protocol = RTPROT_STATIC; /* everything is user-defined */
|
||||
hdr->rtm_scope = RT_SCOPE_LINK; /* TODO: scope in mctp_route? */
|
||||
hdr->rtm_type = rt->type;
|
||||
|
||||
if (nla_put_u8(skb, RTA_DST, rt->min))
|
||||
|
|
@ -1422,13 +1614,17 @@ static int mctp_fill_rtinfo(struct sk_buff *skb, struct mctp_route *rt,
|
|||
|
||||
nla_nest_end(skb, metrics);
|
||||
|
||||
if (rt->dev) {
|
||||
if (rt->dst_type == MCTP_ROUTE_DIRECT) {
|
||||
hdr->rtm_scope = RT_SCOPE_LINK;
|
||||
if (nla_put_u32(skb, RTA_OIF, rt->dev->dev->ifindex))
|
||||
goto cancel;
|
||||
} else if (rt->dst_type == MCTP_ROUTE_GATEWAY) {
|
||||
hdr->rtm_scope = RT_SCOPE_UNIVERSE;
|
||||
if (nla_put(skb, RTA_GATEWAY,
|
||||
sizeof(rt->gateway), &rt->gateway))
|
||||
goto cancel;
|
||||
}
|
||||
|
||||
/* TODO: conditional neighbour physaddr? */
|
||||
|
||||
nlmsg_end(skb, nlh);
|
||||
|
||||
return 0;
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
229
net/mctp/test/sock-test.c
Normal file
229
net/mctp/test/sock-test.c
Normal file
|
|
@ -0,0 +1,229 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
#include <kunit/static_stub.h>
|
||||
#include <kunit/test.h>
|
||||
|
||||
#include <linux/socket.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
static const u8 dev_default_lladdr[] = { 0x01, 0x02 };
|
||||
|
||||
/* helper for simple sock setup: single device, with dev_default_lladdr as its
|
||||
* hardware address, assigned with a local EID 8, and a route to EID 9
|
||||
*/
|
||||
static void __mctp_sock_test_init(struct kunit *test,
|
||||
struct mctp_test_dev **devp,
|
||||
struct mctp_test_route **rtp,
|
||||
struct socket **sockp)
|
||||
{
|
||||
struct mctp_test_route *rt;
|
||||
struct mctp_test_dev *dev;
|
||||
struct socket *sock;
|
||||
unsigned long flags;
|
||||
u8 *addrs;
|
||||
int rc;
|
||||
|
||||
dev = mctp_test_create_dev_lladdr(sizeof(dev_default_lladdr),
|
||||
dev_default_lladdr);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev);
|
||||
|
||||
addrs = kmalloc(1, GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, addrs);
|
||||
addrs[0] = 8;
|
||||
|
||||
spin_lock_irqsave(&dev->mdev->addrs_lock, flags);
|
||||
dev->mdev->num_addrs = 1;
|
||||
swap(addrs, dev->mdev->addrs);
|
||||
spin_unlock_irqrestore(&dev->mdev->addrs_lock, flags);
|
||||
|
||||
kfree(addrs);
|
||||
|
||||
rt = mctp_test_create_route_direct(dev_net(dev->ndev), dev->mdev, 9, 0);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, rt);
|
||||
|
||||
rc = sock_create_kern(&init_net, AF_MCTP, SOCK_DGRAM, 0, &sock);
|
||||
KUNIT_ASSERT_EQ(test, rc, 0);
|
||||
|
||||
*devp = dev;
|
||||
*rtp = rt;
|
||||
*sockp = sock;
|
||||
}
|
||||
|
||||
static void __mctp_sock_test_fini(struct kunit *test,
|
||||
struct mctp_test_dev *dev,
|
||||
struct mctp_test_route *rt,
|
||||
struct socket *sock)
|
||||
{
|
||||
sock_release(sock);
|
||||
mctp_test_route_destroy(test, rt);
|
||||
mctp_test_destroy_dev(dev);
|
||||
}
|
||||
|
||||
struct mctp_test_sock_local_output_config {
|
||||
struct mctp_test_dev *dev;
|
||||
size_t halen;
|
||||
u8 haddr[MAX_ADDR_LEN];
|
||||
bool invoked;
|
||||
int rc;
|
||||
};
|
||||
|
||||
static int mctp_test_sock_local_output(struct sock *sk,
|
||||
struct mctp_dst *dst,
|
||||
struct sk_buff *skb,
|
||||
mctp_eid_t daddr, u8 req_tag)
|
||||
{
|
||||
struct kunit *test = kunit_get_current_test();
|
||||
struct mctp_test_sock_local_output_config *cfg = test->priv;
|
||||
|
||||
KUNIT_EXPECT_PTR_EQ(test, dst->dev, cfg->dev->mdev);
|
||||
KUNIT_EXPECT_EQ(test, dst->halen, cfg->halen);
|
||||
KUNIT_EXPECT_MEMEQ(test, dst->haddr, cfg->haddr, dst->halen);
|
||||
|
||||
cfg->invoked = true;
|
||||
|
||||
kfree_skb(skb);
|
||||
|
||||
return cfg->rc;
|
||||
}
|
||||
|
||||
static void mctp_test_sock_sendmsg_extaddr(struct kunit *test)
|
||||
{
|
||||
struct sockaddr_mctp_ext addr = {
|
||||
.smctp_base = {
|
||||
.smctp_family = AF_MCTP,
|
||||
.smctp_tag = MCTP_TAG_OWNER,
|
||||
.smctp_network = MCTP_NET_ANY,
|
||||
},
|
||||
};
|
||||
struct mctp_test_sock_local_output_config cfg = { 0 };
|
||||
u8 haddr[] = { 0xaa, 0x01 };
|
||||
u8 buf[4] = { 0, 1, 2, 3 };
|
||||
struct mctp_test_route *rt;
|
||||
struct msghdr msg = { 0 };
|
||||
struct mctp_test_dev *dev;
|
||||
struct mctp_sock *msk;
|
||||
struct socket *sock;
|
||||
ssize_t send_len;
|
||||
struct kvec vec = {
|
||||
.iov_base = buf,
|
||||
.iov_len = sizeof(buf),
|
||||
};
|
||||
|
||||
__mctp_sock_test_init(test, &dev, &rt, &sock);
|
||||
|
||||
/* Expect to see the dst configured up with the addressing data we
|
||||
* provide in the struct sockaddr_mctp_ext
|
||||
*/
|
||||
cfg.dev = dev;
|
||||
cfg.halen = sizeof(haddr);
|
||||
memcpy(cfg.haddr, haddr, sizeof(haddr));
|
||||
|
||||
test->priv = &cfg;
|
||||
|
||||
kunit_activate_static_stub(test, mctp_local_output,
|
||||
mctp_test_sock_local_output);
|
||||
|
||||
/* enable and configure direct addressing */
|
||||
msk = container_of(sock->sk, struct mctp_sock, sk);
|
||||
msk->addr_ext = true;
|
||||
|
||||
addr.smctp_ifindex = dev->ndev->ifindex;
|
||||
addr.smctp_halen = sizeof(haddr);
|
||||
memcpy(addr.smctp_haddr, haddr, sizeof(haddr));
|
||||
|
||||
msg.msg_name = &addr;
|
||||
msg.msg_namelen = sizeof(addr);
|
||||
|
||||
iov_iter_kvec(&msg.msg_iter, ITER_SOURCE, &vec, 1, sizeof(buf));
|
||||
send_len = mctp_sendmsg(sock, &msg, sizeof(buf));
|
||||
KUNIT_EXPECT_EQ(test, send_len, sizeof(buf));
|
||||
KUNIT_EXPECT_TRUE(test, cfg.invoked);
|
||||
|
||||
__mctp_sock_test_fini(test, dev, rt, sock);
|
||||
}
|
||||
|
||||
static void mctp_test_sock_recvmsg_extaddr(struct kunit *test)
|
||||
{
|
||||
struct sockaddr_mctp_ext recv_addr = { 0 };
|
||||
u8 rcv_buf[1], rcv_data[] = { 0, 1 };
|
||||
u8 haddr[] = { 0xaa, 0x02 };
|
||||
struct mctp_test_route *rt;
|
||||
struct mctp_test_dev *dev;
|
||||
struct mctp_skb_cb *cb;
|
||||
struct mctp_sock *msk;
|
||||
struct sk_buff *skb;
|
||||
struct mctp_hdr hdr;
|
||||
struct socket *sock;
|
||||
struct msghdr msg;
|
||||
ssize_t recv_len;
|
||||
int rc;
|
||||
struct kvec vec = {
|
||||
.iov_base = rcv_buf,
|
||||
.iov_len = sizeof(rcv_buf),
|
||||
};
|
||||
|
||||
__mctp_sock_test_init(test, &dev, &rt, &sock);
|
||||
|
||||
/* enable extended addressing on recv */
|
||||
msk = container_of(sock->sk, struct mctp_sock, sk);
|
||||
msk->addr_ext = true;
|
||||
|
||||
/* base incoming header, using a nul-EID dest */
|
||||
hdr.ver = 1;
|
||||
hdr.dest = 0;
|
||||
hdr.src = 9;
|
||||
hdr.flags_seq_tag = MCTP_HDR_FLAG_SOM | MCTP_HDR_FLAG_EOM |
|
||||
MCTP_HDR_FLAG_TO;
|
||||
|
||||
skb = mctp_test_create_skb_data(&hdr, &rcv_data);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, skb);
|
||||
|
||||
mctp_test_skb_set_dev(skb, dev);
|
||||
|
||||
/* set incoming extended address data */
|
||||
cb = mctp_cb(skb);
|
||||
cb->halen = sizeof(haddr);
|
||||
cb->ifindex = dev->ndev->ifindex;
|
||||
memcpy(cb->haddr, haddr, sizeof(haddr));
|
||||
|
||||
/* Deliver to socket. The route input path pulls the network header,
|
||||
* leaving skb data at type byte onwards. recvmsg will consume the
|
||||
* type for addr.smctp_type
|
||||
*/
|
||||
skb_pull(skb, sizeof(hdr));
|
||||
rc = sock_queue_rcv_skb(sock->sk, skb);
|
||||
KUNIT_ASSERT_EQ(test, rc, 0);
|
||||
|
||||
msg.msg_name = &recv_addr;
|
||||
msg.msg_namelen = sizeof(recv_addr);
|
||||
iov_iter_kvec(&msg.msg_iter, ITER_DEST, &vec, 1, sizeof(rcv_buf));
|
||||
|
||||
recv_len = mctp_recvmsg(sock, &msg, sizeof(rcv_buf),
|
||||
MSG_DONTWAIT | MSG_TRUNC);
|
||||
|
||||
KUNIT_EXPECT_EQ(test, recv_len, sizeof(rcv_buf));
|
||||
|
||||
/* expect our extended address to be populated from hdr and cb */
|
||||
KUNIT_EXPECT_EQ(test, msg.msg_namelen, sizeof(recv_addr));
|
||||
KUNIT_EXPECT_EQ(test, recv_addr.smctp_base.smctp_family, AF_MCTP);
|
||||
KUNIT_EXPECT_EQ(test, recv_addr.smctp_ifindex, dev->ndev->ifindex);
|
||||
KUNIT_EXPECT_EQ(test, recv_addr.smctp_halen, sizeof(haddr));
|
||||
KUNIT_EXPECT_MEMEQ(test, recv_addr.smctp_haddr, haddr, sizeof(haddr));
|
||||
|
||||
__mctp_sock_test_fini(test, dev, rt, sock);
|
||||
}
|
||||
|
||||
static struct kunit_case mctp_test_cases[] = {
|
||||
KUNIT_CASE(mctp_test_sock_sendmsg_extaddr),
|
||||
KUNIT_CASE(mctp_test_sock_recvmsg_extaddr),
|
||||
{}
|
||||
};
|
||||
|
||||
static struct kunit_suite mctp_test_suite = {
|
||||
.name = "mctp-sock",
|
||||
.test_cases = mctp_test_cases,
|
||||
};
|
||||
|
||||
kunit_test_suite(mctp_test_suite);
|
||||
|
|
@ -26,19 +26,22 @@ static void mctp_test_dev_setup(struct net_device *ndev)
|
|||
ndev->type = ARPHRD_MCTP;
|
||||
ndev->mtu = MCTP_DEV_TEST_MTU;
|
||||
ndev->hard_header_len = 0;
|
||||
ndev->addr_len = 0;
|
||||
ndev->tx_queue_len = DEFAULT_TX_QUEUE_LEN;
|
||||
ndev->flags = IFF_NOARP;
|
||||
ndev->netdev_ops = &mctp_test_netdev_ops;
|
||||
ndev->needs_free_netdev = true;
|
||||
}
|
||||
|
||||
struct mctp_test_dev *mctp_test_create_dev(void)
|
||||
static struct mctp_test_dev *__mctp_test_create_dev(unsigned short lladdr_len,
|
||||
const unsigned char *lladdr)
|
||||
{
|
||||
struct mctp_test_dev *dev;
|
||||
struct net_device *ndev;
|
||||
int rc;
|
||||
|
||||
if (WARN_ON(lladdr_len > MAX_ADDR_LEN))
|
||||
return NULL;
|
||||
|
||||
ndev = alloc_netdev(sizeof(*dev), "mctptest%d", NET_NAME_ENUM,
|
||||
mctp_test_dev_setup);
|
||||
if (!ndev)
|
||||
|
|
@ -46,6 +49,8 @@ struct mctp_test_dev *mctp_test_create_dev(void)
|
|||
|
||||
dev = netdev_priv(ndev);
|
||||
dev->ndev = ndev;
|
||||
ndev->addr_len = lladdr_len;
|
||||
dev_addr_set(ndev, lladdr);
|
||||
|
||||
rc = register_netdev(ndev);
|
||||
if (rc) {
|
||||
|
|
@ -61,8 +66,195 @@ struct mctp_test_dev *mctp_test_create_dev(void)
|
|||
return dev;
|
||||
}
|
||||
|
||||
struct mctp_test_dev *mctp_test_create_dev(void)
|
||||
{
|
||||
return __mctp_test_create_dev(0, NULL);
|
||||
}
|
||||
|
||||
struct mctp_test_dev *mctp_test_create_dev_lladdr(unsigned short lladdr_len,
|
||||
const unsigned char *lladdr)
|
||||
{
|
||||
return __mctp_test_create_dev(lladdr_len, lladdr);
|
||||
}
|
||||
|
||||
void mctp_test_destroy_dev(struct mctp_test_dev *dev)
|
||||
{
|
||||
mctp_dev_put(dev->mdev);
|
||||
unregister_netdev(dev->ndev);
|
||||
}
|
||||
|
||||
static const unsigned int test_pktqueue_magic = 0x5f713aef;
|
||||
|
||||
void mctp_test_pktqueue_init(struct mctp_test_pktqueue *tpq)
|
||||
{
|
||||
tpq->magic = test_pktqueue_magic;
|
||||
skb_queue_head_init(&tpq->pkts);
|
||||
}
|
||||
|
||||
static int mctp_test_dst_output(struct mctp_dst *dst, struct sk_buff *skb)
|
||||
{
|
||||
struct kunit *test = current->kunit_test;
|
||||
struct mctp_test_pktqueue *tpq = test->priv;
|
||||
|
||||
KUNIT_ASSERT_EQ(test, tpq->magic, test_pktqueue_magic);
|
||||
|
||||
skb_queue_tail(&tpq->pkts, skb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* local version of mctp_route_alloc() */
|
||||
static struct mctp_test_route *mctp_route_test_alloc(void)
|
||||
{
|
||||
struct mctp_test_route *rt;
|
||||
|
||||
rt = kzalloc(sizeof(*rt), GFP_KERNEL);
|
||||
if (!rt)
|
||||
return NULL;
|
||||
|
||||
INIT_LIST_HEAD(&rt->rt.list);
|
||||
refcount_set(&rt->rt.refs, 1);
|
||||
rt->rt.output = mctp_test_dst_output;
|
||||
|
||||
return rt;
|
||||
}
|
||||
|
||||
struct mctp_test_route *mctp_test_create_route_direct(struct net *net,
|
||||
struct mctp_dev *dev,
|
||||
mctp_eid_t eid,
|
||||
unsigned int mtu)
|
||||
{
|
||||
struct mctp_test_route *rt;
|
||||
|
||||
rt = mctp_route_test_alloc();
|
||||
if (!rt)
|
||||
return NULL;
|
||||
|
||||
rt->rt.min = eid;
|
||||
rt->rt.max = eid;
|
||||
rt->rt.mtu = mtu;
|
||||
rt->rt.type = RTN_UNSPEC;
|
||||
rt->rt.dst_type = MCTP_ROUTE_DIRECT;
|
||||
if (dev)
|
||||
mctp_dev_hold(dev);
|
||||
rt->rt.dev = dev;
|
||||
|
||||
list_add_rcu(&rt->rt.list, &net->mctp.routes);
|
||||
|
||||
return rt;
|
||||
}
|
||||
|
||||
struct mctp_test_route *mctp_test_create_route_gw(struct net *net,
|
||||
unsigned int netid,
|
||||
mctp_eid_t eid,
|
||||
mctp_eid_t gw,
|
||||
unsigned int mtu)
|
||||
{
|
||||
struct mctp_test_route *rt;
|
||||
|
||||
rt = mctp_route_test_alloc();
|
||||
if (!rt)
|
||||
return NULL;
|
||||
|
||||
rt->rt.min = eid;
|
||||
rt->rt.max = eid;
|
||||
rt->rt.mtu = mtu;
|
||||
rt->rt.type = RTN_UNSPEC;
|
||||
rt->rt.dst_type = MCTP_ROUTE_GATEWAY;
|
||||
rt->rt.gateway.eid = gw;
|
||||
rt->rt.gateway.net = netid;
|
||||
|
||||
list_add_rcu(&rt->rt.list, &net->mctp.routes);
|
||||
|
||||
return rt;
|
||||
}
|
||||
|
||||
/* Convenience function for our test dst; release with mctp_test_dst_release()
|
||||
*/
|
||||
void mctp_test_dst_setup(struct kunit *test, struct mctp_dst *dst,
|
||||
struct mctp_test_dev *dev,
|
||||
struct mctp_test_pktqueue *tpq, unsigned int mtu)
|
||||
{
|
||||
KUNIT_EXPECT_NOT_ERR_OR_NULL(test, dev);
|
||||
|
||||
memset(dst, 0, sizeof(*dst));
|
||||
|
||||
dst->dev = dev->mdev;
|
||||
__mctp_dev_get(dst->dev->dev);
|
||||
dst->mtu = mtu;
|
||||
dst->output = mctp_test_dst_output;
|
||||
mctp_test_pktqueue_init(tpq);
|
||||
test->priv = tpq;
|
||||
}
|
||||
|
||||
void mctp_test_dst_release(struct mctp_dst *dst,
|
||||
struct mctp_test_pktqueue *tpq)
|
||||
{
|
||||
mctp_dst_release(dst);
|
||||
skb_queue_purge(&tpq->pkts);
|
||||
}
|
||||
|
||||
void mctp_test_route_destroy(struct kunit *test, struct mctp_test_route *rt)
|
||||
{
|
||||
unsigned int refs;
|
||||
|
||||
rtnl_lock();
|
||||
list_del_rcu(&rt->rt.list);
|
||||
rtnl_unlock();
|
||||
|
||||
if (rt->rt.dst_type == MCTP_ROUTE_DIRECT && rt->rt.dev)
|
||||
mctp_dev_put(rt->rt.dev);
|
||||
|
||||
refs = refcount_read(&rt->rt.refs);
|
||||
KUNIT_ASSERT_EQ_MSG(test, refs, 1, "route ref imbalance");
|
||||
|
||||
kfree_rcu(&rt->rt, rcu);
|
||||
}
|
||||
|
||||
void mctp_test_skb_set_dev(struct sk_buff *skb, struct mctp_test_dev *dev)
|
||||
{
|
||||
struct mctp_skb_cb *cb;
|
||||
|
||||
cb = mctp_cb(skb);
|
||||
cb->net = READ_ONCE(dev->mdev->net);
|
||||
skb->dev = dev->ndev;
|
||||
}
|
||||
|
||||
struct sk_buff *mctp_test_create_skb(const struct mctp_hdr *hdr,
|
||||
unsigned int data_len)
|
||||
{
|
||||
size_t hdr_len = sizeof(*hdr);
|
||||
struct sk_buff *skb;
|
||||
unsigned int i;
|
||||
u8 *buf;
|
||||
|
||||
skb = alloc_skb(hdr_len + data_len, GFP_KERNEL);
|
||||
if (!skb)
|
||||
return NULL;
|
||||
|
||||
__mctp_cb(skb);
|
||||
memcpy(skb_put(skb, hdr_len), hdr, hdr_len);
|
||||
|
||||
buf = skb_put(skb, data_len);
|
||||
for (i = 0; i < data_len; i++)
|
||||
buf[i] = i & 0xff;
|
||||
|
||||
return skb;
|
||||
}
|
||||
|
||||
struct sk_buff *__mctp_test_create_skb_data(const struct mctp_hdr *hdr,
|
||||
const void *data, size_t data_len)
|
||||
{
|
||||
size_t hdr_len = sizeof(*hdr);
|
||||
struct sk_buff *skb;
|
||||
|
||||
skb = alloc_skb(hdr_len + data_len, GFP_KERNEL);
|
||||
if (!skb)
|
||||
return NULL;
|
||||
|
||||
__mctp_cb(skb);
|
||||
memcpy(skb_put(skb, hdr_len), hdr, hdr_len);
|
||||
memcpy(skb_put(skb, data_len), data, data_len);
|
||||
|
||||
return skb;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,11 @@
|
|||
#ifndef __NET_MCTP_TEST_UTILS_H
|
||||
#define __NET_MCTP_TEST_UTILS_H
|
||||
|
||||
#include <uapi/linux/netdevice.h>
|
||||
|
||||
#include <net/mctp.h>
|
||||
#include <net/mctpdevice.h>
|
||||
|
||||
#include <kunit/test.h>
|
||||
|
||||
#define MCTP_DEV_TEST_MTU 68
|
||||
|
|
@ -10,11 +15,50 @@
|
|||
struct mctp_test_dev {
|
||||
struct net_device *ndev;
|
||||
struct mctp_dev *mdev;
|
||||
|
||||
unsigned short lladdr_len;
|
||||
unsigned char lladdr[MAX_ADDR_LEN];
|
||||
};
|
||||
|
||||
struct mctp_test_dev;
|
||||
|
||||
struct mctp_test_route {
|
||||
struct mctp_route rt;
|
||||
};
|
||||
|
||||
struct mctp_test_pktqueue {
|
||||
unsigned int magic;
|
||||
struct sk_buff_head pkts;
|
||||
};
|
||||
|
||||
struct mctp_test_dev *mctp_test_create_dev(void);
|
||||
struct mctp_test_dev *mctp_test_create_dev_lladdr(unsigned short lladdr_len,
|
||||
const unsigned char *lladdr);
|
||||
void mctp_test_destroy_dev(struct mctp_test_dev *dev);
|
||||
|
||||
struct mctp_test_route *mctp_test_create_route_direct(struct net *net,
|
||||
struct mctp_dev *dev,
|
||||
mctp_eid_t eid,
|
||||
unsigned int mtu);
|
||||
struct mctp_test_route *mctp_test_create_route_gw(struct net *net,
|
||||
unsigned int netid,
|
||||
mctp_eid_t eid,
|
||||
mctp_eid_t gw,
|
||||
unsigned int mtu);
|
||||
void mctp_test_dst_setup(struct kunit *test, struct mctp_dst *dst,
|
||||
struct mctp_test_dev *dev,
|
||||
struct mctp_test_pktqueue *tpq, unsigned int mtu);
|
||||
void mctp_test_dst_release(struct mctp_dst *dst,
|
||||
struct mctp_test_pktqueue *tpq);
|
||||
void mctp_test_pktqueue_init(struct mctp_test_pktqueue *tpq);
|
||||
void mctp_test_route_destroy(struct kunit *test, struct mctp_test_route *rt);
|
||||
void mctp_test_skb_set_dev(struct sk_buff *skb, struct mctp_test_dev *dev);
|
||||
struct sk_buff *mctp_test_create_skb(const struct mctp_hdr *hdr,
|
||||
unsigned int data_len);
|
||||
struct sk_buff *__mctp_test_create_skb_data(const struct mctp_hdr *hdr,
|
||||
const void *data, size_t data_len);
|
||||
|
||||
#define mctp_test_create_skb_data(h, d) \
|
||||
__mctp_test_create_skb_data(h, d, sizeof(*d))
|
||||
|
||||
#endif /* __NET_MCTP_TEST_UTILS_H */
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user