net: ipip: Refactor the IP tunneling support
Refactor the IP tunneling support as the input callback was removed in previous commit. The data will flow through the recv callback now as expected. Signed-off-by: Jukka Rissanen <jukka.rissanen@nordicsemi.no>
This commit is contained in:
parent
2c1f10b60e
commit
280391ded8
5 changed files with 213 additions and 94 deletions
|
|
@ -90,9 +90,9 @@ struct virtual_interface_api {
|
|||
|
||||
/**
|
||||
* Receive a network packet.
|
||||
* The callback returns NET_CONTINUE if this interface will accept the
|
||||
* The callback returns NET_OK if this interface will accept the
|
||||
* packet and pass it upper layers, NET_DROP if the packet is to be
|
||||
* dropped and NET_OK to pass it to next interface.
|
||||
* dropped and NET_CONTINUE to pass it to next interface.
|
||||
*/
|
||||
enum net_verdict (*recv)(struct net_if *iface, struct net_pkt *pkt);
|
||||
|
||||
|
|
|
|||
|
|
@ -246,7 +246,7 @@ enum net_verdict net_ipv4_input(struct net_pkt *pkt, bool is_loopback)
|
|||
uint8_t opts_len;
|
||||
int pkt_len;
|
||||
|
||||
#if defined(CONFIG_NET_L2_VIRTUAL)
|
||||
#if defined(CONFIG_NET_L2_IPIP)
|
||||
struct net_pkt_cursor hdr_start;
|
||||
|
||||
net_pkt_cursor_backup(pkt, &hdr_start);
|
||||
|
|
@ -403,21 +403,27 @@ enum net_verdict net_ipv4_input(struct net_pkt *pkt, bool is_loopback)
|
|||
}
|
||||
break;
|
||||
|
||||
#if defined(CONFIG_NET_L2_VIRTUAL)
|
||||
#if defined(CONFIG_NET_L2_IPIP)
|
||||
case IPPROTO_IPV6:
|
||||
case IPPROTO_IPIP: {
|
||||
struct net_addr remote_addr;
|
||||
struct sockaddr_in remote_addr = { 0 };
|
||||
struct net_if *tunnel_iface;
|
||||
|
||||
remote_addr.family = AF_INET;
|
||||
net_ipv4_addr_copy_raw((uint8_t *)&remote_addr.in_addr, hdr->src);
|
||||
remote_addr.sin_family = AF_INET;
|
||||
net_ipv4_addr_copy_raw((uint8_t *)&remote_addr.sin_addr, hdr->src);
|
||||
|
||||
net_pkt_set_remote_address(pkt, (struct sockaddr *)&remote_addr,
|
||||
sizeof(struct sockaddr_in));
|
||||
|
||||
/* Get rid of the old IP header */
|
||||
net_pkt_cursor_restore(pkt, &hdr_start);
|
||||
net_pkt_pull(pkt, net_pkt_ip_hdr_len(pkt) +
|
||||
net_pkt_ipv4_opts_len(pkt));
|
||||
|
||||
return net_virtual_input(net_pkt_iface(pkt), &remote_addr,
|
||||
pkt);
|
||||
tunnel_iface = net_ipip_get_virtual_interface(net_pkt_iface(pkt));
|
||||
if (tunnel_iface != NULL && net_if_l2(tunnel_iface)->recv != NULL) {
|
||||
return net_if_l2(tunnel_iface)->recv(net_pkt_iface(pkt), pkt);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
|
|||
|
|
@ -466,7 +466,7 @@ enum net_verdict net_ipv6_input(struct net_pkt *pkt, bool is_loopback)
|
|||
union net_ip_header ip;
|
||||
int pkt_len;
|
||||
|
||||
#if defined(CONFIG_NET_L2_VIRTUAL)
|
||||
#if defined(CONFIG_NET_L2_IPIP)
|
||||
struct net_pkt_cursor hdr_start;
|
||||
|
||||
net_pkt_cursor_backup(pkt, &hdr_start);
|
||||
|
|
@ -725,20 +725,28 @@ enum net_verdict net_ipv6_input(struct net_pkt *pkt, bool is_loopback)
|
|||
verdict = NET_OK;
|
||||
}
|
||||
break;
|
||||
#if defined(CONFIG_NET_L2_VIRTUAL)
|
||||
|
||||
#if defined(CONFIG_NET_L2_IPIP)
|
||||
case IPPROTO_IPV6:
|
||||
case IPPROTO_IPIP: {
|
||||
struct net_addr remote_addr;
|
||||
struct sockaddr_in6 remote_addr = { 0 };
|
||||
struct net_if *tunnel_iface;
|
||||
|
||||
remote_addr.family = AF_INET6;
|
||||
net_ipv6_addr_copy_raw((uint8_t *)&remote_addr.in6_addr, hdr->src);
|
||||
remote_addr.sin6_family = AF_INET6;
|
||||
net_ipv6_addr_copy_raw((uint8_t *)&remote_addr.sin6_addr, hdr->src);
|
||||
|
||||
net_pkt_set_remote_address(pkt, (struct sockaddr *)&remote_addr,
|
||||
sizeof(struct sockaddr_in6));
|
||||
|
||||
/* Get rid of the old IP header */
|
||||
net_pkt_cursor_restore(pkt, &hdr_start);
|
||||
net_pkt_pull(pkt, net_pkt_ip_hdr_len(pkt) +
|
||||
net_pkt_ipv6_ext_len(pkt));
|
||||
|
||||
return net_virtual_input(pkt_iface, &remote_addr, pkt);
|
||||
tunnel_iface = net_ipip_get_virtual_interface(net_pkt_iface(pkt));
|
||||
if (tunnel_iface != NULL && net_if_l2(tunnel_iface)->recv != NULL) {
|
||||
return net_if_l2(tunnel_iface)->recv(net_pkt_iface(pkt), pkt);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
|
|||
|
|
@ -69,6 +69,8 @@ extern int net_icmp_call_ipv6_handlers(struct net_pkt *pkt,
|
|||
struct net_ipv6_hdr *ipv6_hdr,
|
||||
struct net_icmp_hdr *icmp_hdr);
|
||||
|
||||
extern struct net_if *net_ipip_get_virtual_interface(struct net_if *input_iface);
|
||||
|
||||
#if defined(CONFIG_NET_NATIVE) || defined(CONFIG_NET_OFFLOAD)
|
||||
extern void net_context_init(void);
|
||||
extern const char *net_context_state(struct net_context *context);
|
||||
|
|
|
|||
|
|
@ -32,6 +32,10 @@ LOG_MODULE_REGISTER(net_virtual_ipip, CONFIG_NET_L2_IPIP_LOG_LEVEL);
|
|||
|
||||
#define PKT_ALLOC_TIME K_MSEC(50)
|
||||
|
||||
static K_MUTEX_DEFINE(lock);
|
||||
|
||||
static void init_context_iface(void);
|
||||
|
||||
struct ipip_context {
|
||||
struct net_if *iface;
|
||||
struct net_if *attached_to;
|
||||
|
|
@ -45,10 +49,20 @@ struct ipip_context {
|
|||
const struct in6_addr *my6addr;
|
||||
};
|
||||
|
||||
bool is_used;
|
||||
bool status;
|
||||
bool init_done;
|
||||
};
|
||||
|
||||
static int virt_dev_init(const struct device *dev)
|
||||
{
|
||||
ARG_UNUSED(dev);
|
||||
|
||||
init_context_iface();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void iface_init(struct net_if *iface)
|
||||
{
|
||||
struct ipip_context *ctx = net_if_get_device(iface)->data;
|
||||
|
|
@ -252,9 +266,65 @@ out:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static bool verify_remote_addr(struct ipip_context *ctx,
|
||||
struct sockaddr *remote_addr)
|
||||
{
|
||||
if (ctx->family != remote_addr->sa_family) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ctx->family == AF_INET) {
|
||||
if (memcmp(&ctx->peer.in_addr, &net_sin(remote_addr)->sin_addr,
|
||||
sizeof(struct in_addr)) == 0) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
if (memcmp(&ctx->peer.in6_addr, &net_sin6(remote_addr)->sin6_addr,
|
||||
sizeof(struct in6_addr)) == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static enum net_verdict interface_recv(struct net_if *iface,
|
||||
struct net_pkt *pkt)
|
||||
{
|
||||
struct ipip_context *ctx = net_if_get_device(iface)->data;
|
||||
struct net_pkt_cursor hdr_start;
|
||||
uint8_t iptype;
|
||||
|
||||
net_pkt_cursor_backup(pkt, &hdr_start);
|
||||
|
||||
if (net_pkt_read_u8(pkt, &iptype)) {
|
||||
return NET_DROP;
|
||||
}
|
||||
|
||||
net_pkt_cursor_restore(pkt, &hdr_start);
|
||||
|
||||
switch (iptype & 0xf0) {
|
||||
case 0x60:
|
||||
net_pkt_set_family(pkt, AF_INET6);
|
||||
break;
|
||||
case 0x40:
|
||||
net_pkt_set_family(pkt, AF_INET);
|
||||
break;
|
||||
default:
|
||||
return NET_DROP;
|
||||
}
|
||||
|
||||
/* Make sure we are receiving data from remote end of the
|
||||
* tunnel. See RFC4213 chapter 4 for details.
|
||||
*/
|
||||
if (!verify_remote_addr(ctx, net_pkt_remote_address(pkt))) {
|
||||
NET_DBG("DROP: remote address %s unknown",
|
||||
net_pkt_remote_address(pkt)->sa_family == AF_INET6 ?
|
||||
net_sprint_ipv6_addr(&net_sin6(net_pkt_remote_address(pkt))->sin6_addr) :
|
||||
net_sprint_ipv4_addr(&net_sin(net_pkt_remote_address(pkt))->sin_addr));
|
||||
return NET_DROP;
|
||||
}
|
||||
|
||||
if (DEBUG_RX) {
|
||||
char str[sizeof("RX iface xx")];
|
||||
|
||||
|
|
@ -264,53 +334,15 @@ static enum net_verdict interface_recv(struct net_if *iface,
|
|||
net_pkt_hexdump(pkt, str);
|
||||
}
|
||||
|
||||
return NET_CONTINUE;
|
||||
}
|
||||
|
||||
static bool verify_remote_addr(struct ipip_context *ctx,
|
||||
struct net_addr *remote_addr)
|
||||
{
|
||||
if (ctx->family != remote_addr->family) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ctx->family == AF_INET) {
|
||||
if (memcmp(&ctx->peer.in_addr, &remote_addr->in_addr,
|
||||
sizeof(struct in_addr)) == 0) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
if (memcmp(&ctx->peer.in6_addr, &remote_addr->in6_addr,
|
||||
sizeof(struct in6_addr)) == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static enum net_verdict interface_input(struct net_if *input_iface,
|
||||
struct net_if *virtual_iface,
|
||||
struct net_addr *remote_addr,
|
||||
struct net_pkt *pkt)
|
||||
{
|
||||
struct ipip_context *ctx = net_if_get_device(virtual_iface)->data;
|
||||
struct net_if *iface;
|
||||
|
||||
/* Make sure we are receiving data from remote end of the
|
||||
* tunnel. See RFC4213 chapter 4 for details.
|
||||
*/
|
||||
if (!verify_remote_addr(ctx, remote_addr)) {
|
||||
NET_DBG("DROP: remote address unknown");
|
||||
return NET_DROP;
|
||||
}
|
||||
|
||||
/* net_pkt cursor must point to correct place so that we can fetch
|
||||
* the network header.
|
||||
*/
|
||||
if (IS_ENABLED(CONFIG_NET_IPV6) && net_pkt_family(pkt) == AF_INET6) {
|
||||
NET_PKT_DATA_ACCESS_DEFINE(access, struct net_ipv6_hdr);
|
||||
struct net_ipv6_hdr *hdr;
|
||||
struct net_if *iface_test;
|
||||
|
||||
net_pkt_cursor_backup(pkt, &hdr_start);
|
||||
|
||||
hdr = (struct net_ipv6_hdr *)net_pkt_get_data(pkt, &access);
|
||||
if (!hdr) {
|
||||
|
|
@ -318,22 +350,17 @@ static enum net_verdict interface_input(struct net_if *input_iface,
|
|||
}
|
||||
|
||||
/* RFC4213 chapter 3.6 */
|
||||
iface = net_if_ipv6_select_src_iface((struct in6_addr *)hdr->dst);
|
||||
if (iface == NULL) {
|
||||
iface_test = net_if_ipv6_select_src_iface((struct in6_addr *)hdr->dst);
|
||||
if (iface_test == NULL) {
|
||||
NET_DBG("DROP: not for me (dst %s)",
|
||||
net_sprint_ipv6_addr(&hdr->dst));
|
||||
return NET_DROP;
|
||||
}
|
||||
|
||||
if (!net_if_is_up(iface)) {
|
||||
NET_DBG("DROP: interface %d down",
|
||||
net_if_get_by_iface(iface));
|
||||
return NET_DROP;
|
||||
}
|
||||
|
||||
if (input_iface != ctx->attached_to ||
|
||||
virtual_iface != iface) {
|
||||
NET_DBG("DROP: wrong interface");
|
||||
if (iface != iface_test) {
|
||||
NET_DBG("DROP: wrong interface %d (%p), expecting %d (%p)",
|
||||
net_if_get_by_iface(iface_test), iface_test,
|
||||
net_if_get_by_iface(iface), iface);
|
||||
return NET_DROP;
|
||||
}
|
||||
|
||||
|
|
@ -345,38 +372,56 @@ static enum net_verdict interface_input(struct net_if *input_iface,
|
|||
|
||||
net_pkt_set_iface(pkt, iface);
|
||||
|
||||
return net_if_recv_data(iface, pkt);
|
||||
net_pkt_cursor_restore(pkt, &hdr_start);
|
||||
|
||||
return net_ipv6_input(pkt, false);
|
||||
}
|
||||
|
||||
if (IS_ENABLED(CONFIG_NET_IPV4) && net_pkt_family(pkt) == AF_INET) {
|
||||
NET_PKT_DATA_ACCESS_DEFINE(access, struct net_ipv4_hdr);
|
||||
struct net_ipv4_hdr *hdr;
|
||||
struct net_if *iface_test;
|
||||
uint16_t sum;
|
||||
|
||||
net_pkt_cursor_backup(pkt, &hdr_start);
|
||||
|
||||
hdr = (struct net_ipv4_hdr *)net_pkt_get_data(pkt, &access);
|
||||
if (!hdr) {
|
||||
return NET_DROP;
|
||||
}
|
||||
|
||||
iface = net_if_ipv4_select_src_iface((struct in_addr *)hdr->dst);
|
||||
if (iface == NULL) {
|
||||
iface_test = net_if_ipv4_select_src_iface((struct in_addr *)hdr->dst);
|
||||
if (iface_test == NULL) {
|
||||
NET_DBG("DROP: not for me (dst %s)",
|
||||
net_sprint_ipv4_addr(&hdr->dst));
|
||||
return NET_DROP;
|
||||
}
|
||||
|
||||
if (!net_if_is_up(iface)) {
|
||||
NET_DBG("DROP: interface %d down",
|
||||
net_if_get_by_iface(iface));
|
||||
if (iface != iface_test) {
|
||||
NET_DBG("DROP: wrong interface %d (%p), expecting %d (%p)",
|
||||
net_if_get_by_iface(iface_test), iface_test,
|
||||
net_if_get_by_iface(iface), iface);
|
||||
return NET_DROP;
|
||||
}
|
||||
|
||||
/* TTL fields is decremented, RFC2003 chapter 3.1 */
|
||||
hdr->ttl--;
|
||||
|
||||
/* Recalculate the checksum because TTL was changed */
|
||||
hdr->chksum = 0U;
|
||||
|
||||
sum = calc_chksum(0, access.data, access.size);
|
||||
sum = (sum == 0U) ? 0xffff : htons(sum);
|
||||
|
||||
hdr->chksum = ~sum;
|
||||
|
||||
(void)net_pkt_set_data(pkt, &access);
|
||||
|
||||
net_pkt_set_iface(pkt, iface);
|
||||
|
||||
return net_if_recv_data(iface, pkt);
|
||||
net_pkt_cursor_restore(pkt, &hdr_start);
|
||||
|
||||
return net_ipv4_input(pkt, false);
|
||||
}
|
||||
|
||||
return NET_CONTINUE;
|
||||
|
|
@ -390,25 +435,35 @@ static int interface_attach(struct net_if *iface, struct net_if *lower_iface)
|
|||
return -ENOENT;
|
||||
}
|
||||
|
||||
k_mutex_lock(&lock, K_FOREVER);
|
||||
|
||||
ctx = net_if_get_device(iface)->data;
|
||||
ctx->attached_to = lower_iface;
|
||||
ctx->iface = iface;
|
||||
|
||||
if (IS_ENABLED(CONFIG_NET_IPV6) && ctx->family == AF_INET6) {
|
||||
struct net_if_addr *ifaddr;
|
||||
struct in6_addr iid;
|
||||
if (lower_iface == NULL) {
|
||||
ctx->is_used = false;
|
||||
} else {
|
||||
ctx->is_used = true;
|
||||
|
||||
/* RFC4213 chapter 3.7 */
|
||||
net_ipv6_addr_create_iid(&iid, net_if_get_link_addr(iface));
|
||||
if (IS_ENABLED(CONFIG_NET_IPV6) && ctx->family == AF_INET6) {
|
||||
struct net_if_addr *ifaddr;
|
||||
struct in6_addr iid;
|
||||
|
||||
ifaddr = net_if_ipv6_addr_add(iface, &iid, NET_ADDR_AUTOCONF,
|
||||
0);
|
||||
if (!ifaddr) {
|
||||
NET_ERR("Cannot add %s address to interface %p",
|
||||
net_sprint_ipv6_addr(&iid),
|
||||
iface);
|
||||
/* RFC4213 chapter 3.7 */
|
||||
net_ipv6_addr_create_iid(&iid, net_if_get_link_addr(iface));
|
||||
|
||||
ifaddr = net_if_ipv6_addr_add(iface, &iid, NET_ADDR_AUTOCONF, 0);
|
||||
if (!ifaddr) {
|
||||
NET_ERR("Cannot add %s address to interface %p",
|
||||
net_sprint_ipv6_addr(&iid),
|
||||
iface);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
k_mutex_unlock(&lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -541,21 +596,69 @@ static const struct virtual_interface_api ipip_iface_api = {
|
|||
.stop = interface_stop,
|
||||
.send = interface_send,
|
||||
.recv = interface_recv,
|
||||
.input = interface_input,
|
||||
.attach = interface_attach,
|
||||
.set_config = interface_set_config,
|
||||
.get_config = interface_get_config,
|
||||
};
|
||||
|
||||
#define NET_IPIP_DATA(x, _) \
|
||||
#define NET_IPIP_INTERFACE_INIT(x, _) \
|
||||
static struct ipip_context ipip_context_data_##x = { \
|
||||
}; \
|
||||
NET_VIRTUAL_INTERFACE_INIT_INSTANCE(ipip_##x, \
|
||||
"IP_TUNNEL" #x, \
|
||||
x, \
|
||||
virt_dev_init, \
|
||||
NULL, \
|
||||
&ipip_context_data_##x, \
|
||||
NULL, /* config */ \
|
||||
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, \
|
||||
&ipip_iface_api, \
|
||||
IPIPV4_MTU)
|
||||
|
||||
LISTIFY(CONFIG_NET_L2_IPIP_TUNNEL_COUNT, NET_IPIP_INTERFACE_INIT, (;), _);
|
||||
|
||||
#define INIT_IPIP_CONTEXT_PTR(x, _) \
|
||||
[x] = &ipip_context_data_##x
|
||||
|
||||
static struct ipip_context *ipip_ctx[] = {
|
||||
LISTIFY(CONFIG_NET_L2_IPIP_TUNNEL_COUNT, INIT_IPIP_CONTEXT_PTR, (,), _)
|
||||
};
|
||||
|
||||
#define INIT_IPIP_CONTEXT_IFACE(x, _) \
|
||||
ipip_context_data_##x.iface = NET_IF_GET(ipip_##x, x)
|
||||
|
||||
static void init_context_iface(void)
|
||||
{
|
||||
static bool init_done;
|
||||
|
||||
if (init_done) {
|
||||
return;
|
||||
}
|
||||
|
||||
#define NET_IPIP_INTERFACE_INIT(x, _) \
|
||||
NET_VIRTUAL_INTERFACE_INIT(ipip##x, "IP_TUNNEL" #x, NULL, \
|
||||
NULL, &ipip_context_data_##x, NULL, \
|
||||
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, \
|
||||
&ipip_iface_api, IPIPV4_MTU)
|
||||
init_done = true;
|
||||
|
||||
LISTIFY(CONFIG_NET_L2_IPIP_TUNNEL_COUNT, NET_IPIP_DATA, (;), _);
|
||||
LISTIFY(CONFIG_NET_L2_IPIP_TUNNEL_COUNT, NET_IPIP_INTERFACE_INIT, (;), _);
|
||||
LISTIFY(CONFIG_NET_L2_IPIP_TUNNEL_COUNT, INIT_IPIP_CONTEXT_IFACE, (;), _);
|
||||
}
|
||||
|
||||
struct net_if *net_ipip_get_virtual_interface(struct net_if *input_iface)
|
||||
{
|
||||
struct net_if *iface = NULL;
|
||||
|
||||
k_mutex_lock(&lock, K_FOREVER);
|
||||
|
||||
ARRAY_FOR_EACH(ipip_ctx, i) {
|
||||
if (ipip_ctx[i] == NULL || !ipip_ctx[i]->is_used) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (input_iface == ipip_ctx[i]->attached_to) {
|
||||
iface = ipip_ctx[i]->iface;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
k_mutex_unlock(&lock);
|
||||
|
||||
return iface;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue