Linux Audio

Check our new training course

Embedded Linux Audio

Check our new training course
with Creative Commons CC-BY-SA
lecture materials

Bootlin logo

Elixir Cross Referencer

Loading...
/*
 * Copyright (c) 2019 Intel Corporation.
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#include <logging/log.h>
LOG_MODULE_REGISTER(net_mgmt_sock_sample, LOG_LEVEL_DBG);

#include <zephyr.h>
#include <errno.h>
#include <stdio.h>
#include <net/socket.h>
#include <net/socket_net_mgmt.h>
#include <net/net_if.h>

#define MAX_BUF_LEN 64
#define STACK_SIZE 1024
#define THREAD_PRIORITY K_PRIO_COOP(8)

/* A test thread that spits out events that we can catch and show to user */
static void trigger_events(void)
{
	int operation = 0;
	struct net_if_addr *ifaddr_v6;
	struct net_if *iface;
	struct in6_addr addr_v6;
	int ret;

	iface = net_if_get_default();

	net_ipv6_addr_create(&addr_v6, 0x2001, 0x0db8, 0, 0, 0, 0, 0, 0x0003);

	while (1) {
		switch (operation) {
		case 0:
			ifaddr_v6 = net_if_ipv6_addr_add(iface, &addr_v6,
							 NET_ADDR_MANUAL, 0);
			if (!ifaddr_v6) {
				LOG_ERR("Cannot add IPv%c address", '6');
				break;
			}

			break;
		case 1:
			ret = net_if_ipv6_addr_rm(iface, &addr_v6);
			if (!ret) {
				LOG_ERR("Cannot del IPv%c address", '6');
				break;
			}

			break;
		default:
			operation = -1;
			break;
		}

		operation++;

		k_sleep(K_SECONDS(1));
	}
}

K_THREAD_DEFINE(trigger_events_thread_id, STACK_SIZE,
		trigger_events, NULL, NULL, NULL,
		THREAD_PRIORITY, 0, -1);

static char *get_ip_addr(char *ipaddr, size_t len, sa_family_t family,
			 struct net_mgmt_msghdr *hdr)
{
	char *buf;

	buf = net_addr_ntop(family, hdr->nm_msg, ipaddr, len);
	if (!buf) {
		return "?";
	}

	return buf;
}

static void listener(void)
{
	struct sockaddr_nm sockaddr;
	struct sockaddr_nm event_addr;
	socklen_t event_addr_len;
	char ipaddr[INET6_ADDRSTRLEN];
	u8_t buf[MAX_BUF_LEN];
	int fd, ret;

	fd = socket(AF_NET_MGMT, SOCK_DGRAM, NET_MGMT_EVENT_PROTO);
	if (fd < 0) {
		printk("Cannot create net_mgmt socket (%d)\n", errno);
		exit(1);
	}

	memset(&sockaddr, 0, sizeof(sockaddr));

	sockaddr.nm_family = AF_NET_MGMT;
	sockaddr.nm_ifindex = 0; /* Any network interface */
	sockaddr.nm_pid = (uintptr_t)k_current_get();
	sockaddr.nm_mask = NET_EVENT_IPV6_DAD_SUCCEED |
			    NET_EVENT_IPV6_ADDR_ADD |
			    NET_EVENT_IPV6_ADDR_DEL;

	ret = bind(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr));
	if (ret < 0) {
		printk("Cannot bind net_mgmt socket (%d)\n", errno);
		exit(1);
	}

	while (1) {
		struct net_mgmt_msghdr *hdr;

		memset(buf, 0, sizeof(buf));
		event_addr_len = sizeof(event_addr);

		ret = recvfrom(fd, buf, sizeof(buf), 0,
			       (struct sockaddr *)&event_addr,
			       &event_addr_len);
		if (ret < 0) {
			continue;
		}

		hdr = (struct net_mgmt_msghdr *)buf;

		if (hdr->nm_msg_version != NET_MGMT_SOCKET_VERSION_1) {
			/* Do not know how to parse the message */
			continue;
		}

		switch (event_addr.nm_mask) {
		case NET_EVENT_IPV6_DAD_SUCCEED:
			printk("DAD succeed for interface %d (%s)\n",
			       event_addr.nm_ifindex,
			       get_ip_addr(ipaddr, sizeof(ipaddr),
					   AF_INET6, hdr));
			break;
		case NET_EVENT_IPV6_ADDR_ADD:
			printk("IPv6 address added to interface %d (%s)\n",
			       event_addr.nm_ifindex,
			       get_ip_addr(ipaddr, sizeof(ipaddr),
					   AF_INET6, hdr));
			break;
		case NET_EVENT_IPV6_ADDR_DEL:
			printk("IPv6 address removed from interface %d (%s)\n",
			       event_addr.nm_ifindex,
			       get_ip_addr(ipaddr, sizeof(ipaddr),
					   AF_INET6, hdr));
			break;
		}
	}
}

void main(void)
{
	/* The thread start to trigger network management events that
	 * we then can catch.
	 */
	k_thread_start(trigger_events_thread_id);

	if (IS_ENABLED(CONFIG_USERSPACE)) {
		k_thread_user_mode_enter((k_thread_entry_t)listener,
					 NULL, NULL, NULL);
	} else {
		listener();
	}
}