summaryrefslogtreecommitdiffstats
path: root/src/device-bridge.c
blob: 11b24b028e3264e505b4617f31e90c62af7abe02 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
#include "device-common.h"
#include "netlink.h"
#include "util.h"

#include <string.h>

#include <net/if.h>

#include <linux/if.h>
#include <linux/if_link.h>
#include <linux/rtnetlink.h>

static device_type_t device_type_bridge;

typedef struct _device_bridge {
	device_t device;
	device_common_t common;
} device_bridge_t;

static void bridge_free(device_t *dev) {
	device_bridge_t *iface = container_of(dev, device_bridge_t, device);

	VECTOR_FREE(iface->common.addrs);
	free(NODE_NAME(dev));
	free(iface);
}

static device_t * bridge_process_config(const char *name, struct json_object *config) {
	device_bridge_t *iface = calloc(1, sizeof(*iface));
	if (!iface)
		return NULL;

	device_t *dev = &iface->device;
	dev->type = &device_type_bridge;

	NODE_NAME(dev) = strdup(name);
	if (!NODE_NAME(dev)) {
		free(iface);
		return NULL;
	}

	if (!device_common_process_config(&iface->common, config))
		goto err;

	return dev;

err:
	bridge_free(dev);
	return NULL;
}

static bool bridge_nl_create(device_bridge_t *bridge) {
	char buf[MNL_SOCKET_BUFFER_SIZE];

	struct mnl_socket *nl = nl_socket();

	struct nlmsghdr *nlh = mnl_nlmsg_put_header(buf);
	nlh->nlmsg_type	= RTM_NEWLINK;
	nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_CREATE;
	int seq = nlh->nlmsg_seq = nl_seq();

	struct ifinfomsg *ifi = mnl_nlmsg_put_extra_header(nlh, sizeof(*ifi));
	ifi->ifi_family = AF_UNSPEC;

	mnl_attr_put_str(nlh, IFLA_IFNAME, NODE_NAME(&bridge->device));

	struct nlattr *linkinfo = mnl_attr_nest_start(nlh, IFLA_LINKINFO);
	mnl_attr_put_str(nlh, IFLA_INFO_KIND, "bridge");
	mnl_attr_nest_end(nlh, linkinfo);

	if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
		perror("mnl_socket_sendto");
		return false;
	}

	int ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
	if (ret == -1) {
		perror("mnl_socket_recvfrom");
		return false;
	}

	ret = mnl_cb_run(buf, ret, seq, mnl_socket_get_portid(nl), NULL, NULL);
	if (ret == -1) {
		perror("mnl_cb_run");
		return false;
	}

	return true;
}

static void bridge_init(device_t *dev) {
	device_bridge_t *bridge = container_of(dev, device_bridge_t, device);

	if (!bridge_nl_create(bridge))
		return;

	unsigned ifindex = if_nametoindex(NODE_NAME(dev));
	if (!ifindex)
		return;

	device_common_init(&bridge->common, ifindex);
}

static void bridge_update(device_t *dev) {
}

static void bridge_release(device_t *dev) {
	/*
	device_bridge_t *bridge = container_of(dev, device_bridge_t, device);

	unsigned ifindex = if_nametoindex(NODE_NAME(dev));
	if (!ifindex)
		return;

	device_common_release(&bridge->common, ifindex);
	*/
}

static device_type_t device_type_bridge = {
	.process_config = bridge_process_config,
	.free = bridge_free,

	.init = bridge_init,
	.update = bridge_update,
	.release = bridge_release,
};

__attribute__((constructor))
static void bridge_constructor(void) {
	register_device_type("bridge", &device_type_bridge);
}