Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2d6a930c authored by Subash Abhinov Kasiviswanathan's avatar Subash Abhinov Kasiviswanathan Committed by Sharath Chandra Vurukala
Browse files

net: qualcomm: rmnet: Introduce IP route mode



This mode is for pure pass through of IP. DFC will continue to
work with QMAP commands.

Change-Id: If9bda0dfab0eacd5a818de1164ef8b00b7073a7a
Signed-off-by: default avatarSubash Abhinov Kasiviswanathan <subashab@codeaurora.org>
parent a397b9fb
Loading
Loading
Loading
Loading
+154 −4
Original line number Diff line number Diff line
@@ -14,9 +14,11 @@
 */

#include <net/sock.h>
#include <net/addrconf.h>
#include <linux/module.h>
#include <linux/netlink.h>
#include <linux/netdevice.h>
#include <linux/inetdevice.h>
#include "rmnet_config.h"
#include "rmnet_handlers.h"
#include "rmnet_vnd.h"
@@ -178,6 +180,9 @@ static int rmnet_newlink(struct net *src_net, struct net_device *dev,
	int err = 0;
	u16 mux_id;

	data_format = RMNET_INGRESS_FORMAT_IP_ROUTE |
		      RMNET_EGRESS_FORMAT_IP_ROUTE;

	real_dev = __dev_get_by_index(src_net, nla_get_u32(tb[IFLA_LINK]));
	if (!real_dev || !dev)
		return -ENODEV;
@@ -715,6 +720,132 @@ EXPORT_SYMBOL(rmnet_get_real_dev);

#endif

struct rmnet_endpoint *rmnet_get_ip6_route_endpoint(struct rmnet_port *port,
						    struct in6_addr *addr)
{
	struct rmnet_endpoint *ep, *tmp = NULL;

	hlist_for_each_entry_rcu(ep, &port->muxed_ep[0], hlnode) {
		if (!memcmp(&ep->in6addr, addr, sizeof(struct in6_addr)))
			return ep;

		tmp = ep;
	}

	return tmp;
}

struct rmnet_endpoint *rmnet_get_ip4_route_endpoint(struct rmnet_port *port,
						    __be32 *ifa_address)
{
	struct rmnet_endpoint *ep, *tmp = NULL;

	hlist_for_each_entry_rcu(ep, &port->muxed_ep[0], hlnode) {
		if (!memcmp(&ep->ifa_address, ifa_address, sizeof(__be32)))
			return ep;

		tmp = ep;
	}

	return tmp;
}

static int rmnet_addr6_event(struct notifier_block *unused,
			     unsigned long event, void *ptr)
{
	struct inet6_ifaddr *if6 = (struct inet6_ifaddr *)ptr;
	struct net_device *dev = (struct net_device *)if6->idev->dev;
	struct rmnet_endpoint *ep;
	struct net_device *real_dev;
	struct rmnet_priv *priv;
	struct rmnet_port *port;

	if (!netif_is_rmnet(dev))
		return NOTIFY_OK;

	priv = netdev_priv(dev);
	real_dev = priv->real_dev;
	port = rmnet_get_port_rtnl(real_dev);

	if (!port || port->data_format & ~(RMNET_INGRESS_FORMAT_IP_ROUTE |
					  RMNET_EGRESS_FORMAT_IP_ROUTE))
		return NOTIFY_OK;

	switch (event) {
	case NETDEV_UP:
		ep = kzalloc(sizeof(*ep), GFP_ATOMIC);
		if (!ep)
			return NOTIFY_OK;

		memcpy(&ep->in6addr, &if6->addr, sizeof(struct in6_addr));
		ep->egress_dev = dev;

		hlist_add_head_rcu(&ep->hlnode, &port->muxed_ep[0]);
		break;
	case NETDEV_DOWN:
		ep = rmnet_get_ip6_route_endpoint(port, &if6->addr);
		if (!ep)
			return NOTIFY_OK;

		hlist_del_init_rcu(&ep->hlnode);
		kfree(ep);
	}

	return NOTIFY_OK;
}

static int rmnet_addr4_event(struct notifier_block *unused,
			     unsigned long event, void *ptr)
{
	struct in_ifaddr *if4 = (struct in_ifaddr *)ptr;
	struct net_device *dev = (struct net_device *)if4->ifa_dev->dev;
	struct rmnet_endpoint *ep;
	struct net_device *real_dev;
	struct rmnet_priv *priv;
	struct rmnet_port *port;

	if (!netif_is_rmnet(dev))
		return NOTIFY_OK;

	priv = netdev_priv(dev);
	real_dev = priv->real_dev;
	port = rmnet_get_port_rtnl(real_dev);

	if (!port || port->data_format & ~(RMNET_INGRESS_FORMAT_IP_ROUTE |
					  RMNET_EGRESS_FORMAT_IP_ROUTE))
		return NOTIFY_OK;

	switch (event) {
	case NETDEV_UP:
		ep = kzalloc(sizeof(*ep), GFP_ATOMIC);
		if (!ep)
			return NOTIFY_OK;

		memcpy(&ep->ifa_address, &if4->ifa_address, sizeof(__be32));
		ep->egress_dev = dev;

		hlist_add_head_rcu(&ep->hlnode, &port->muxed_ep[0]);
		break;
	case NETDEV_DOWN:
		ep = rmnet_get_ip4_route_endpoint(port, &if4->ifa_address);
		if (!ep)
			return NOTIFY_OK;

		hlist_del_init_rcu(&ep->hlnode);
		kfree(ep);
	}

	return NOTIFY_OK;
}

static struct notifier_block rmnet_addr6_notifier_block __read_mostly = {
	.notifier_call = rmnet_addr6_event,
};

static struct notifier_block rmnet_addr4_notifier_block __read_mostly = {
	.notifier_call = rmnet_addr4_event,
};

/* Startup/Shutdown */

static int __init rmnet_init(void)
@@ -726,15 +857,34 @@ static int __init rmnet_init(void)
		return rc;

	rc = rtnl_link_register(&rmnet_link_ops);
	if (rc != 0) {
	if (rc != 0)
		goto err0;

	rc = register_inet6addr_notifier(&rmnet_addr6_notifier_block);
	if (rc != 0)
		goto err1;

	rc = register_inetaddr_notifier(&rmnet_addr4_notifier_block);
	if (rc != 0)
		goto err2;

	return 0;

err2:
	unregister_inet6addr_notifier(&rmnet_addr6_notifier_block);

err1:
	rtnl_link_unregister(&rmnet_link_ops);

err0:
	unregister_netdevice_notifier(&rmnet_dev_notifier);
	return rc;
}
	return rc;
}

static void __exit rmnet_exit(void)
{
	unregister_inetaddr_notifier(&rmnet_addr4_notifier_block);
	unregister_inet6addr_notifier(&rmnet_addr6_notifier_block);
	unregister_netdevice_notifier(&rmnet_dev_notifier);
	rtnl_link_unregister(&rmnet_link_ops);
}
+15 −1
Original line number Diff line number Diff line
@@ -23,7 +23,11 @@
#define RMNET_MAX_VEID 4

struct rmnet_endpoint {
	union {
		u8 mux_id;
		__be32 ifa_address;
		struct in6_addr in6addr;
	};
	struct net_device *egress_dev;
	struct hlist_node hlnode;
};
@@ -33,6 +37,11 @@ struct rmnet_agg_stats {
	u64 ul_agg_alloc;
};

struct rmnet_ip_route_endpoint {
	struct in6_addr addr;
	struct net_device *egress_dev;
};

struct rmnet_port_priv_stats {
	u64 dl_hdr_last_qmap_vers;
	u64 dl_hdr_last_ep_id;
@@ -191,4 +200,9 @@ int rmnet_add_bridge(struct net_device *rmnet_dev,
		     struct net_device *slave_dev);
int rmnet_del_bridge(struct net_device *rmnet_dev,
		     struct net_device *slave_dev);

struct rmnet_endpoint *rmnet_get_ip6_route_endpoint(struct rmnet_port *port,
						    struct in6_addr *addr);
struct rmnet_endpoint *rmnet_get_ip4_route_endpoint(struct rmnet_port *port,
						    __be32 *ifa_address);
#endif /* _RMNET_CONFIG_H_ */
+48 −0
Original line number Diff line number Diff line
@@ -210,6 +210,44 @@ static void rmnet_deliver_skb_list(struct sk_buff_head *head,
	}
}

static void rmnet_ip_route_rcv(struct sk_buff *skb, struct rmnet_port *port)
{
	struct rmnet_endpoint *ep;

	skb_reset_transport_header(skb);
	skb_reset_network_header(skb);

	skb->pkt_type = PACKET_HOST;
	skb_set_mac_header(skb, 0);

	switch (rmnet_map_data_ptr(skb)[0] & 0xF0) {
	case RMNET_IP_VERSION_4:
		skb->protocol = htons(ETH_P_IP);
		ep = rmnet_get_ip4_route_endpoint(port, &(ip_hdr(skb)->daddr));
		if (!ep)
			goto drop_skb;
		break;
	case RMNET_IP_VERSION_6:
		skb->protocol = htons(ETH_P_IPV6);
		ep = rmnet_get_ip6_route_endpoint(port,
						  &(ipv6_hdr(skb)->daddr));
		if (!ep)
			goto drop_skb;
		break;
	default:
		goto drop_skb;
	}

	skb->dev = ep->egress_dev;
	rmnet_vnd_rx_fixup(skb->dev, skb->len);

	netif_receive_skb(skb);
	return;

drop_skb:
	kfree_skb(skb);
}

/* MAP handler */

static void
@@ -225,6 +263,11 @@ __rmnet_map_ingress_handler(struct sk_buff *skb,
	/* We don't need the spinlock since only we touch this */
	__skb_queue_head_init(&list);

	if (port->data_format & RMNET_INGRESS_FORMAT_IP_ROUTE) {
		rmnet_ip_route_rcv(skb, port);
		return;
	}

	qmap = (struct rmnet_map_header *)rmnet_map_data_ptr(skb);
	if (qmap->cd_bit) {
		qmi_rmnet_set_dl_msg_active(port);
@@ -472,6 +515,10 @@ void rmnet_egress_handler(struct sk_buff *skb)
		goto drop;

	skb_len = skb->len;

	if (port->data_format & RMNET_EGRESS_FORMAT_IP_ROUTE)
		goto direct_xmit;

	err = rmnet_map_egress_handler(skb, port, mux_id, orig_dev);
	if (err == -ENOMEM)
		goto drop;
@@ -480,6 +527,7 @@ void rmnet_egress_handler(struct sk_buff *skb)
		return;
	}

direct_xmit:
	rmnet_vnd_tx_fixup(orig_dev, skb_len);

	dev_queue_xmit(skb);
+4 −0
Original line number Diff line number Diff line
@@ -33,6 +33,10 @@ RMNET_INGRESS_FORMAT_DL_MARKER_V2)
/* UL Aggregation parameters */
#define RMNET_PAGE_RECYCLE                      BIT(0)

/* IP-Mux feature */
#define RMNET_INGRESS_FORMAT_IP_ROUTE           BIT(25)
#define RMNET_EGRESS_FORMAT_IP_ROUTE            BIT(24)

/* Replace skb->dev to a virtual rmnet device and pass up the stack */
#define RMNET_EPMODE_VND (1)
/* Pass the frame directly to another device with dev_queue_xmit() */
+5 −0
Original line number Diff line number Diff line
@@ -407,3 +407,8 @@ int rmnet_vnd_do_flow_control(struct net_device *rmnet_dev, int enable)

	return 0;
}

int netif_is_rmnet(const struct net_device *dev)
{
	return dev->netdev_ops == &rmnet_vnd_ops;
}
Loading