-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathunshare-example.c
202 lines (169 loc) · 4.11 KB
/
unshare-example.c
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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
#define _GNU_SOURCE
#include <stdio.h>
#include <asm/types.h>
#include <sys/socket.h>
#include <string.h>
#include <unistd.h>
#include <sched.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
/* seq # */
int seq = 0;
/* a chunky buffer that seems to be big enough for my system */
char buf[8192];
/* Make a rtnetlink socket, bind it to our PID, set the peer to be the kernel.
*/
int makesocket()
{
int nls = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
if (nls == -1)
{
perror("socket");
return 1;
}
// set our address
struct sockaddr_nl sa;
memset(&sa, 0, sizeof(sa));
sa.nl_family = AF_NETLINK;
sa.nl_pid = getpid();
if (bind(nls, (struct sockaddr *)&sa, sizeof(sa)) == -1)
{
perror("bind");
return -1;
}
// set their (kernel) address
struct sockaddr_nl sa_kernel;
memset(&sa_kernel, 0, sizeof(sa_kernel));
sa_kernel.nl_family = AF_NETLINK;
if (connect(nls, (struct sockaddr *)&sa_kernel, sizeof(sa_kernel)) == -1)
{
perror("connect");
return -1;
}
return nls;
}
/* Request a dump of all links.
*/
int reqdump(int nls)
{
int seqq = seq++;
struct {
struct nlmsghdr h;
struct ifinfomsg m;
} msg;
memset(&msg, 0, sizeof(msg));
msg.h.nlmsg_len = NLMSG_LENGTH(sizeof(msg.m));
msg.h.nlmsg_type = RTM_GETLINK;
msg.h.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
msg.h.nlmsg_seq = seqq;
msg.h.nlmsg_pid = getpid();
// explicitly set these even tho we memzero'd
msg.m.ifi_family = 0;
msg.m.ifi_type = 0;
msg.m.ifi_index = 0;
msg.m.ifi_flags = 0;
msg.m.ifi_change = 0xFFFFFFFF;
// send the datagram
int res = send(nls, &msg, msg.h.nlmsg_len, 0);
if (res == -1)
{
perror("send");
return -1;
}
return seqq;
}
/* Receives and processes one datagram from the rtnetlink socket.
*/
int receive_one_datagram(int nls)
{
ssize_t s = recv(nls, &buf, sizeof(buf), 0);
if (s == -1)
{
perror("recv");
return -1;
}
int seen_the_end = 0;
struct nlmsghdr * nh;
struct rtattr * rta;
// note: at some point, NLMSG_OK will return false and we will stop iterating without having seen
// NLMSG_DONE. this means we have more datagrams to process. this wasn't clear to me from the
// documentation, but indeed the kernel behaves this way.
for (nh = (struct nlmsghdr *)buf; NLMSG_OK(nh, s); nh = NLMSG_NEXT(nh, s))
{
if (nh->nlmsg_type == NLMSG_DONE)
{
seen_the_end = 1;
break;
}
else if (nh->nlmsg_type == NLMSG_ERROR)
{
printf("error\n");
return -1;
}
else if (nh->nlmsg_type == NLMSG_NOOP || nh->nlmsg_type != RTM_NEWLINK)
{
continue;
}
// message type RTM_NEWLINK is ifinfomsg + 0 or more rtattr
struct ifinfomsg * ifinfo = NLMSG_DATA(nh);
ssize_t s2 = NLMSG_PAYLOAD(nh, s) - sizeof(*ifinfo);
char * data = NLMSG_DATA(nh) + sizeof(*ifinfo);
for (rta = (struct rtattr *) data; RTA_OK(rta, s); rta = RTA_NEXT(rta, s2))
{
char * ifname;
switch (rta->rta_type)
{
case IFLA_IFNAME:
ifname = (char *)RTA_DATA(rta);
printf("ifin: %d, ifname: %s\n", ifinfo->ifi_index, ifname);
break;
}
}
}
return !seen_the_end;
}
/* Prints all network interfaces on stdout
*/
int print_all_interfaces(int nls)
{
int seqq = reqdump(nls);
if (seqq == -1)
return 2;
/* it's not clear from the documentation and not really shown in the manual examples that a DUMP
* request might actually get replied to with more than one datagram. in other words, the "byte
* stream mentioned in netlink(7) might span more than one datagram. */
int more;
do {
more = receive_one_datagram(nls);
}
while (more);
return 0;
}
/* Get a fresh network namespace.
*/
int change_network_ns()
{
if (unshare(CLONE_NEWNET) == -1)
{
perror("unshare");
return 1;
}
return 0;
}
int main()
{
printf("before unshare:\n");
int nls = makesocket();
if (nls == -1)
return 1;
print_all_interfaces(nls);
close(nls);
change_network_ns();
printf("after unshare:\n");
nls = makesocket();
if (nls == -1)
return 1;
print_all_interfaces(nls);
close(nls);
return 0;
}