mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/chenhuacai/linux-loongson
synced 2025-08-28 18:10:32 +00:00

The addition of connect() adds new conflict cases to test. Signed-off-by: Matt Johnston <matt@codeconstruct.com.au> Link: https://patch.msgid.link/20250710-mctp-bind-v4-7-8ec2f6460c56@codeconstruct.com.au Signed-off-by: Paolo Abeni <pabeni@redhat.com>
297 lines
6.4 KiB
C
297 lines
6.4 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
|
|
#include <linux/netdevice.h>
|
|
#include <linux/mctp.h>
|
|
#include <linux/if_arp.h>
|
|
|
|
#include <net/mctp.h>
|
|
#include <net/mctpdevice.h>
|
|
#include <net/pkt_sched.h>
|
|
|
|
#include "utils.h"
|
|
|
|
static netdev_tx_t mctp_test_dev_tx(struct sk_buff *skb,
|
|
struct net_device *ndev)
|
|
{
|
|
kfree_skb(skb);
|
|
return NETDEV_TX_OK;
|
|
}
|
|
|
|
static const struct net_device_ops mctp_test_netdev_ops = {
|
|
.ndo_start_xmit = mctp_test_dev_tx,
|
|
};
|
|
|
|
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->tx_queue_len = DEFAULT_TX_QUEUE_LEN;
|
|
ndev->flags = IFF_NOARP;
|
|
ndev->netdev_ops = &mctp_test_netdev_ops;
|
|
ndev->needs_free_netdev = true;
|
|
}
|
|
|
|
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)
|
|
return NULL;
|
|
|
|
dev = netdev_priv(ndev);
|
|
dev->ndev = ndev;
|
|
ndev->addr_len = lladdr_len;
|
|
dev_addr_set(ndev, lladdr);
|
|
|
|
rc = register_netdev(ndev);
|
|
if (rc) {
|
|
free_netdev(ndev);
|
|
return NULL;
|
|
}
|
|
|
|
rcu_read_lock();
|
|
dev->mdev = __mctp_dev_get(ndev);
|
|
dev->mdev->net = mctp_default_net(dev_net(ndev));
|
|
rcu_read_unlock();
|
|
|
|
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;
|
|
}
|
|
|
|
void mctp_test_bind_run(struct kunit *test,
|
|
const struct mctp_test_bind_setup *setup,
|
|
int *ret_bind_errno, struct socket **sock)
|
|
{
|
|
struct sockaddr_mctp addr;
|
|
int rc;
|
|
|
|
*ret_bind_errno = -EIO;
|
|
|
|
rc = sock_create_kern(&init_net, AF_MCTP, SOCK_DGRAM, 0, sock);
|
|
KUNIT_ASSERT_EQ(test, rc, 0);
|
|
|
|
/* connect() if requested */
|
|
if (setup->have_peer) {
|
|
memset(&addr, 0x0, sizeof(addr));
|
|
addr.smctp_family = AF_MCTP;
|
|
addr.smctp_network = setup->peer_net;
|
|
addr.smctp_addr.s_addr = setup->peer_addr;
|
|
/* connect() type must match bind() type */
|
|
addr.smctp_type = setup->bind_type;
|
|
rc = kernel_connect(*sock, (struct sockaddr *)&addr,
|
|
sizeof(addr), 0);
|
|
KUNIT_EXPECT_EQ(test, rc, 0);
|
|
}
|
|
|
|
/* bind() */
|
|
memset(&addr, 0x0, sizeof(addr));
|
|
addr.smctp_family = AF_MCTP;
|
|
addr.smctp_network = setup->bind_net;
|
|
addr.smctp_addr.s_addr = setup->bind_addr;
|
|
addr.smctp_type = setup->bind_type;
|
|
|
|
*ret_bind_errno =
|
|
kernel_bind(*sock, (struct sockaddr *)&addr, sizeof(addr));
|
|
}
|