]> git.proxmox.com Git - ceph.git/blame - ceph/src/spdk/lib/net/interface.c
update sources to ceph Nautilus 14.2.1
[ceph.git] / ceph / src / spdk / lib / net / interface.c
CommitLineData
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
47static TAILQ_HEAD(, spdk_interface) g_interface_head;
48
49static pthread_mutex_t interface_lock = PTHREAD_MUTEX_INITIALIZER;
50
11fdf7f2 51static 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
153exit:
154 close(netlink_fd);
155 return ret;
156}
157
158
11fdf7f2 159static 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 199static 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 }
302exit:
303 close(netlink_fd);
304 return ret;
305}
306
307static 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
323static 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
411static 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 424int
7c673cae
FG
425spdk_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
438void
439spdk_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
450int
451spdk_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
459int
460spdk_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
468void *spdk_interface_get_list(void)
469{
470 spdk_interface_ip_update();
471 return &g_interface_head;
472}
473
474#else /* Not Linux */
475
11fdf7f2 476int
7c673cae
FG
477spdk_interface_init(void)
478{
479 return 0;
480}
481
11fdf7f2 482void
7c673cae
FG
483spdk_interface_destroy(void)
484{
7c673cae
FG
485}
486
487int
488spdk_interface_add_ip_address(int ifc_index, char *ip_addr)
489{
490 return -1;
491}
492
493int
494spdk_interface_delete_ip_address(int ifc_index, char *ip_addr)
495{
496 return -1;
497}
498
499void *
500spdk_interface_get_list(void)
501{
502 return NULL;
503}
504
505#endif