]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | /*- |
2 | * BSD LICENSE | |
3 | * | |
4 | * Copyright (c) Intel Corporation. | |
5 | * All rights reserved. | |
6 | * | |
7 | * Redistribution and use in source and binary forms, with or without | |
8 | * modification, are permitted provided that the following conditions | |
9 | * are met: | |
10 | * | |
11 | * * Redistributions of source code must retain the above copyright | |
12 | * notice, this list of conditions and the following disclaimer. | |
13 | * * Redistributions in binary form must reproduce the above copyright | |
14 | * notice, this list of conditions and the following disclaimer in | |
15 | * the documentation and/or other materials provided with the | |
16 | * distribution. | |
17 | * * Neither the name of Intel Corporation nor the names of its | |
18 | * contributors may be used to endorse or promote products derived | |
19 | * from this software without specific prior written permission. | |
20 | * | |
21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
22 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
23 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
24 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
25 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
26 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
27 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
28 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
29 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
30 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
31 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
32 | */ | |
33 | ||
11fdf7f2 TL |
34 | #include "net_internal.h" |
35 | ||
36 | #include "spdk/stdinc.h" | |
37 | #include "spdk/string.h" | |
7c673cae FG |
38 | |
39 | #include "spdk/log.h" | |
40 | #include "spdk/net.h" | |
41 | ||
7c673cae FG |
42 | #ifdef __linux__ /* Interface management is Linux-specific */ |
43 | ||
7c673cae FG |
44 | #include <linux/netlink.h> |
45 | #include <linux/rtnetlink.h> | |
7c673cae FG |
46 | |
47 | static TAILQ_HEAD(, spdk_interface) g_interface_head; | |
48 | ||
49 | static pthread_mutex_t interface_lock = PTHREAD_MUTEX_INITIALIZER; | |
50 | ||
11fdf7f2 | 51 | static int spdk_get_ifc_ipv4(void) |
7c673cae FG |
52 | { |
53 | int ret; | |
54 | int rtattrlen; | |
55 | int netlink_fd; | |
56 | uint32_t ipv4_addr; | |
57 | ||
58 | struct { | |
59 | struct nlmsghdr n; | |
60 | struct ifaddrmsg r; | |
61 | struct rtattr rta; | |
62 | } req; | |
63 | char buf[16384]; | |
64 | struct nlmsghdr *nlmp; | |
65 | struct ifaddrmsg *rtmp; | |
66 | struct rtattr *rtatp; | |
67 | struct spdk_interface *ifc; | |
68 | ||
69 | netlink_fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); | |
70 | if (netlink_fd < 0) { | |
71 | SPDK_ERRLOG("socket failed!\n"); | |
72 | return 1; | |
73 | } | |
74 | ||
75 | /* | |
76 | * Prepare a message structure | |
77 | */ | |
78 | memset(&req, 0, sizeof(req)); | |
79 | req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg)); | |
80 | req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_ROOT; | |
81 | req.n.nlmsg_type = RTM_GETADDR; | |
82 | ||
83 | /* IPv4 only */ | |
84 | req.r.ifa_family = AF_INET; | |
85 | ||
86 | /* | |
87 | * Fill up all the attributes for the rtnetlink header. | |
88 | */ | |
89 | assert(&req.rta == (struct rtattr *)(((char *)&req) + NLMSG_ALIGN(req.n.nlmsg_len))); | |
90 | req.rta.rta_len = RTA_LENGTH(16); | |
91 | ||
92 | /* Send and recv the message from kernel */ | |
93 | ret = send(netlink_fd, &req, req.n.nlmsg_len, 0); | |
94 | if (ret < 0) { | |
11fdf7f2 | 95 | SPDK_ERRLOG("netlink send failed: %s\n", spdk_strerror(errno)); |
7c673cae FG |
96 | ret = 1; |
97 | goto exit; | |
98 | } | |
99 | ||
100 | ret = recv(netlink_fd, buf, sizeof(buf), 0); | |
101 | if (ret <= 0) { | |
11fdf7f2 | 102 | SPDK_ERRLOG("netlink recv failed: %s\n", spdk_strerror(errno)); |
7c673cae FG |
103 | ret = 1; |
104 | goto exit; | |
105 | } | |
106 | ||
107 | for (nlmp = (struct nlmsghdr *)buf; ret > (int)sizeof(*nlmp);) { | |
108 | int len = nlmp->nlmsg_len; | |
109 | int req_len = len - sizeof(*nlmp); | |
110 | ||
111 | if (req_len < 0 || len > ret) { | |
112 | SPDK_ERRLOG("error\n"); | |
113 | ret = 1; | |
114 | goto exit; | |
115 | } | |
116 | ||
117 | if (!NLMSG_OK(nlmp, (uint32_t)ret)) { | |
118 | SPDK_ERRLOG("NLMSG not OK\n"); | |
119 | ret = 1; | |
120 | goto exit; | |
121 | } | |
122 | ||
123 | rtmp = (struct ifaddrmsg *)NLMSG_DATA(nlmp); | |
124 | rtatp = (struct rtattr *)IFA_RTA(rtmp); | |
125 | ||
126 | rtattrlen = IFA_PAYLOAD(nlmp); | |
127 | ||
128 | for (; RTA_OK(rtatp, rtattrlen); rtatp = RTA_NEXT(rtatp, rtattrlen)) { | |
129 | if (rtatp->rta_type == IFA_LOCAL) { | |
130 | memcpy(&ipv4_addr, (struct in_addr *)RTA_DATA(rtatp), | |
131 | sizeof(struct in_addr)); | |
132 | TAILQ_FOREACH(ifc, &g_interface_head, tailq) { | |
133 | if (ifc->index == rtmp->ifa_index) { | |
134 | /* add a new IP address to interface */ | |
135 | if (ifc->num_ip_addresses >= SPDK_MAX_IP_PER_IFC) { | |
136 | SPDK_ERRLOG("SPDK: number of IP addresses supported for %s excceded. limit=%d\n", | |
137 | ifc->name, | |
138 | SPDK_MAX_IP_PER_IFC); | |
139 | break; | |
140 | } | |
141 | ifc->ip_address[ifc->num_ip_addresses] = ipv4_addr; | |
142 | ifc->num_ip_addresses++; | |
143 | break; | |
144 | } | |
145 | } | |
146 | } | |
147 | } | |
148 | ret -= NLMSG_ALIGN(len); | |
149 | nlmp = (struct nlmsghdr *)((char *)nlmp + NLMSG_ALIGN(len)); | |
150 | } | |
151 | ret = 0; | |
152 | ||
153 | exit: | |
154 | close(netlink_fd); | |
155 | return ret; | |
156 | } | |
157 | ||
158 | ||
11fdf7f2 | 159 | static int spdk_process_new_interface_msg(struct nlmsghdr *h) |
7c673cae FG |
160 | { |
161 | int len; | |
162 | struct spdk_interface *ifc; | |
163 | struct ifinfomsg *iface; | |
164 | struct rtattr *attribute; | |
165 | ||
166 | iface = (struct ifinfomsg *)NLMSG_DATA(h); | |
167 | ||
168 | ifc = (struct spdk_interface *) malloc(sizeof(*ifc)); | |
169 | if (ifc == NULL) { | |
170 | SPDK_ERRLOG("%s: Malloc failed\n", __func__); | |
11fdf7f2 | 171 | return 1; |
7c673cae FG |
172 | } |
173 | ||
174 | memset(ifc, 0, sizeof(*ifc)); | |
175 | ||
176 | /* Set interface index */ | |
177 | ifc->index = iface->ifi_index; | |
178 | ||
179 | len = h->nlmsg_len - NLMSG_LENGTH(sizeof(*iface)); | |
180 | ||
181 | /* Loop over all attributes for the NEWLINK message */ | |
182 | for (attribute = IFLA_RTA(iface); RTA_OK(attribute, len); attribute = RTA_NEXT(attribute, len)) { | |
183 | switch (attribute->rta_type) { | |
184 | case IFLA_IFNAME: | |
185 | if (if_indextoname(iface->ifi_index, ifc->name) == NULL) { | |
186 | SPDK_ERRLOG("Indextoname failed!\n"); | |
11fdf7f2 TL |
187 | free(ifc); |
188 | return 2; | |
7c673cae FG |
189 | } |
190 | break; | |
191 | default: | |
192 | break; | |
193 | } | |
194 | } | |
195 | TAILQ_INSERT_TAIL(&g_interface_head, ifc, tailq); | |
11fdf7f2 | 196 | return 0; |
7c673cae FG |
197 | } |
198 | ||
11fdf7f2 | 199 | static int spdk_prepare_ifc_list(void) |
7c673cae | 200 | { |
11fdf7f2 | 201 | int ret = 0; |
7c673cae FG |
202 | struct nl_req_s { |
203 | struct nlmsghdr hdr; | |
204 | struct rtgenmsg gen; | |
205 | struct ifinfomsg ifi; | |
206 | }; | |
207 | int netlink_fd; | |
11fdf7f2 TL |
208 | struct sockaddr_nl local; /* Our local (user space) side of the communication */ |
209 | struct sockaddr_nl kernel; /* The remote (kernel space) side of the communication */ | |
7c673cae | 210 | |
11fdf7f2 TL |
211 | struct msghdr rtnl_msg; /* Generic msghdr struct for use with sendmsg */ |
212 | struct iovec io; /* IO vector for sendmsg */ | |
7c673cae | 213 | |
11fdf7f2 TL |
214 | struct nl_req_s req; /* Structure that describes the rtnetlink packet itself */ |
215 | char reply[16384]; /* a large buffer to receive lots of link information */ | |
7c673cae | 216 | |
11fdf7f2 TL |
217 | pid_t pid = getpid(); /* Our process ID to build the correct netlink address */ |
218 | int end = 0; /* some flag to end loop parsing */ | |
7c673cae FG |
219 | |
220 | /* | |
221 | * Prepare netlink socket for kernel/user space communication | |
222 | */ | |
223 | netlink_fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); | |
224 | if (netlink_fd < 0) { | |
225 | SPDK_ERRLOG("socket failed!\n"); | |
226 | return 1; | |
227 | } | |
228 | ||
229 | memset(&local, 0, sizeof(local)); /* Fill-in local address information */ | |
230 | local.nl_family = AF_NETLINK; | |
231 | local.nl_pid = pid; | |
232 | local.nl_groups = 0; | |
233 | ||
234 | /* RTNL socket is ready to use, prepare and send L2 request. */ | |
235 | memset(&rtnl_msg, 0, sizeof(rtnl_msg)); | |
236 | memset(&kernel, 0, sizeof(kernel)); | |
237 | memset(&req, 0, sizeof(req)); | |
238 | ||
239 | kernel.nl_family = AF_NETLINK; /* Fill-in kernel address (destination of our message) */ | |
240 | ||
241 | req.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtgenmsg)); | |
242 | req.hdr.nlmsg_type = RTM_GETLINK; | |
243 | req.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; | |
244 | req.hdr.nlmsg_seq = 1; | |
245 | req.hdr.nlmsg_pid = pid; | |
246 | ||
247 | req.ifi.ifi_family = AF_UNSPEC; | |
248 | req.ifi.ifi_type = 1; | |
249 | ||
250 | io.iov_base = &req; | |
251 | io.iov_len = req.hdr.nlmsg_len; | |
252 | rtnl_msg.msg_iov = &io; | |
253 | rtnl_msg.msg_iovlen = 1; | |
254 | rtnl_msg.msg_name = &kernel; | |
255 | rtnl_msg.msg_namelen = sizeof(kernel); | |
256 | ||
257 | if (sendmsg(netlink_fd, &rtnl_msg, 0) == -1) { | |
258 | SPDK_ERRLOG("Sendmsg failed!\n"); | |
259 | ret = 1; | |
260 | goto exit; | |
261 | } | |
262 | ||
263 | /* Parse reply */ | |
264 | while (!end) { | |
265 | int len; | |
266 | struct nlmsghdr *msg_ptr; /* Pointer to current message part */ | |
267 | ||
268 | struct msghdr rtnl_reply; /* Generic msghdr structure for use with recvmsg */ | |
269 | struct iovec io_reply; | |
270 | ||
271 | memset(&io_reply, 0, sizeof(io_reply)); | |
272 | memset(&rtnl_reply, 0, sizeof(rtnl_reply)); | |
273 | ||
274 | io.iov_base = reply; | |
275 | io.iov_len = 8192; | |
276 | rtnl_reply.msg_iov = &io; | |
277 | rtnl_reply.msg_iovlen = 1; | |
278 | rtnl_reply.msg_name = &kernel; | |
279 | rtnl_reply.msg_namelen = sizeof(kernel); | |
280 | ||
281 | /* Read as much data as fits in the receive buffer */ | |
282 | len = recvmsg(netlink_fd, &rtnl_reply, 0); | |
283 | if (len) { | |
284 | for (msg_ptr = (struct nlmsghdr *) reply; NLMSG_OK(msg_ptr, (uint32_t)len); | |
285 | msg_ptr = NLMSG_NEXT(msg_ptr, len)) { | |
286 | switch (msg_ptr->nlmsg_type) { | |
287 | case NLMSG_DONE: /* This is the special meaning NLMSG_DONE message we asked for by using NLM_F_DUMP flag */ | |
288 | end++; | |
289 | break; | |
290 | case RTM_NEWLINK: /* This is a RTM_NEWLINK message, which contains lots of information about a link */ | |
11fdf7f2 TL |
291 | ret = spdk_process_new_interface_msg(msg_ptr); |
292 | if (ret != 0) { | |
293 | goto exit; | |
294 | } | |
7c673cae FG |
295 | break; |
296 | default: | |
297 | break; | |
298 | } | |
299 | } | |
300 | } | |
301 | } | |
302 | exit: | |
303 | close(netlink_fd); | |
304 | return ret; | |
305 | } | |
306 | ||
307 | static int spdk_interface_available(uint32_t ifc_index) | |
308 | { | |
309 | struct spdk_interface *ifc_entry; | |
310 | ||
311 | pthread_mutex_lock(&interface_lock); | |
312 | TAILQ_FOREACH(ifc_entry, &g_interface_head, tailq) { | |
313 | if (ifc_entry->index == ifc_index) { | |
314 | pthread_mutex_unlock(&interface_lock); | |
315 | return 0; | |
316 | } | |
317 | } | |
318 | pthread_mutex_unlock(&interface_lock); | |
319 | ||
320 | return -1; | |
321 | } | |
322 | ||
323 | static int netlink_addr_msg(uint32_t ifc_idx, uint32_t ip_address, uint32_t create) | |
324 | { | |
325 | int fd; | |
326 | struct sockaddr_nl la; | |
327 | struct sockaddr_nl pa; | |
328 | struct msghdr msg; | |
329 | struct iovec iov; | |
330 | int ifal; | |
331 | struct { | |
332 | struct nlmsghdr n; | |
333 | struct ifaddrmsg r; | |
334 | char buf[16384]; | |
335 | } req; | |
336 | struct rtattr *rta; | |
337 | ||
11fdf7f2 | 338 | if (spdk_interface_available(ifc_idx)) { |
7c673cae | 339 | return -1; |
11fdf7f2 | 340 | } |
7c673cae FG |
341 | |
342 | fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); | |
343 | if (fd < 0) { | |
344 | SPDK_ERRLOG("socket failed!\n"); | |
345 | return -1; | |
346 | } | |
347 | ||
348 | /* setup local address & bind using this address. */ | |
349 | bzero(&la, sizeof(la)); | |
350 | la.nl_family = AF_NETLINK; | |
351 | la.nl_pid = getpid(); | |
352 | bind(fd, (struct sockaddr *) &la, sizeof(la)); | |
353 | ||
11fdf7f2 | 354 | /* initialize RTNETLINK request buffer. */ |
7c673cae FG |
355 | bzero(&req, sizeof(req)); |
356 | ||
357 | /* compute the initial length of the service request. */ | |
358 | ifal = sizeof(struct ifaddrmsg); | |
359 | ||
360 | /* add first attrib: set IP addr and RTNETLINK buffer size. */ | |
361 | rta = (struct rtattr *) req.buf; | |
362 | rta->rta_type = IFA_ADDRESS; | |
363 | rta->rta_len = sizeof(struct rtattr) + 4; | |
364 | memcpy(((char *)rta) + sizeof(struct rtattr), &ip_address, sizeof(ip_address)); | |
365 | ifal += rta->rta_len; | |
366 | ||
367 | /* add second attrib. */ | |
368 | rta = (struct rtattr *)(((char *)rta) + rta->rta_len); | |
369 | rta->rta_type = IFA_LOCAL; | |
370 | rta->rta_len = sizeof(struct rtattr) + 4; | |
371 | memcpy(((char *)rta) + sizeof(struct rtattr), &ip_address, sizeof(ip_address)); | |
372 | ifal += rta->rta_len; | |
373 | ||
374 | /* setup the NETLINK header. */ | |
375 | req.n.nlmsg_len = NLMSG_LENGTH(ifal); | |
376 | if (create) { | |
377 | req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_APPEND; | |
378 | req.n.nlmsg_type = RTM_NEWADDR; | |
379 | } else { | |
380 | req.n.nlmsg_flags = NLM_F_REQUEST; | |
381 | req.n.nlmsg_type = RTM_DELADDR; | |
382 | } | |
383 | ||
384 | /* setup the service header (struct rtmsg). */ | |
385 | req.r.ifa_family = AF_INET; | |
11fdf7f2 | 386 | req.r.ifa_prefixlen = 32; /* hardcoded */ |
7c673cae FG |
387 | req.r.ifa_flags = IFA_F_PERMANENT | IFA_F_SECONDARY; |
388 | req.r.ifa_index = ifc_idx; | |
389 | req.r.ifa_scope = 0; | |
390 | ||
391 | /* create the remote address to communicate. */ | |
392 | bzero(&pa, sizeof(pa)); | |
393 | pa.nl_family = AF_NETLINK; | |
394 | ||
395 | /* initialize & create the struct msghdr supplied to the sendmsg() function. */ | |
396 | bzero(&msg, sizeof(msg)); | |
397 | msg.msg_name = (void *) &pa; | |
398 | msg.msg_namelen = sizeof(pa); | |
399 | ||
400 | /* place the pointer & size of the RTNETLINK message in the struct msghdr. */ | |
401 | iov.iov_base = (void *) &req.n; | |
402 | iov.iov_len = req.n.nlmsg_len; | |
403 | msg.msg_iov = &iov; | |
404 | msg.msg_iovlen = 1; | |
405 | /* send the RTNETLINK message to kernel. */ | |
406 | sendmsg(fd, &msg, 0); | |
407 | close(fd); | |
408 | return 0; | |
409 | } | |
410 | ||
411 | static void spdk_interface_ip_update(void) | |
412 | { | |
413 | struct spdk_interface *ifc_entry; | |
414 | ||
415 | pthread_mutex_lock(&interface_lock); | |
416 | TAILQ_FOREACH(ifc_entry, &g_interface_head, tailq) { | |
417 | ifc_entry->num_ip_addresses = 0; | |
418 | memset(ifc_entry->ip_address, 0, sizeof(ifc_entry->ip_address)); | |
419 | } | |
420 | spdk_get_ifc_ipv4(); | |
421 | pthread_mutex_unlock(&interface_lock); | |
422 | } | |
423 | ||
11fdf7f2 | 424 | int |
7c673cae FG |
425 | spdk_interface_init(void) |
426 | { | |
11fdf7f2 TL |
427 | int rc = 0; |
428 | ||
7c673cae | 429 | TAILQ_INIT(&g_interface_head); |
11fdf7f2 TL |
430 | rc = spdk_prepare_ifc_list(); |
431 | if (!rc) { | |
432 | rc = spdk_get_ifc_ipv4(); | |
433 | } | |
434 | ||
435 | return rc; | |
7c673cae FG |
436 | } |
437 | ||
11fdf7f2 TL |
438 | void |
439 | spdk_interface_destroy(void) | |
7c673cae FG |
440 | { |
441 | struct spdk_interface *ifc_entry; | |
442 | ||
443 | while (!TAILQ_EMPTY(&g_interface_head)) { | |
444 | ifc_entry = TAILQ_FIRST(&g_interface_head); | |
445 | TAILQ_REMOVE(&g_interface_head, ifc_entry, tailq); | |
446 | free(ifc_entry); | |
447 | } | |
7c673cae FG |
448 | } |
449 | ||
450 | int | |
451 | spdk_interface_add_ip_address(int ifc_index, char *ip_addr) | |
452 | { | |
453 | uint32_t addr; | |
454 | ||
455 | addr = inet_addr(ip_addr); | |
456 | return netlink_addr_msg(ifc_index, addr, 1); | |
457 | } | |
458 | ||
459 | int | |
460 | spdk_interface_delete_ip_address(int ifc_index, char *ip_addr) | |
461 | { | |
462 | uint32_t addr; | |
463 | ||
464 | addr = inet_addr(ip_addr); | |
465 | return netlink_addr_msg(ifc_index, addr, 0); | |
466 | } | |
467 | ||
468 | void *spdk_interface_get_list(void) | |
469 | { | |
470 | spdk_interface_ip_update(); | |
471 | return &g_interface_head; | |
472 | } | |
473 | ||
474 | #else /* Not Linux */ | |
475 | ||
11fdf7f2 | 476 | int |
7c673cae FG |
477 | spdk_interface_init(void) |
478 | { | |
479 | return 0; | |
480 | } | |
481 | ||
11fdf7f2 | 482 | void |
7c673cae FG |
483 | spdk_interface_destroy(void) |
484 | { | |
7c673cae FG |
485 | } |
486 | ||
487 | int | |
488 | spdk_interface_add_ip_address(int ifc_index, char *ip_addr) | |
489 | { | |
490 | return -1; | |
491 | } | |
492 | ||
493 | int | |
494 | spdk_interface_delete_ip_address(int ifc_index, char *ip_addr) | |
495 | { | |
496 | return -1; | |
497 | } | |
498 | ||
499 | void * | |
500 | spdk_interface_get_list(void) | |
501 | { | |
502 | return NULL; | |
503 | } | |
504 | ||
505 | #endif |