1 /* Determine whether interfaces use native transport.  Linux version.
2    Copyright (C) 2007-2022 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4 
5    The GNU C Library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public
7    License as published by the Free Software Foundation; either
8    version 2.1 of the License, or (at your option) any later version.
9 
10    The GNU C Library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Lesser General Public License for more details.
14 
15    You should have received a copy of the GNU Lesser General Public
16    License along with the GNU C Library; if not, see
17    <https://www.gnu.org/licenses/>.  */
18 
19 #include <assert.h>
20 #include <errno.h>
21 #include <ifaddrs.h>
22 #include <stddef.h>
23 #include <stdint.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <time.h>
27 #include <unistd.h>
28 #include <net/if.h>
29 #include <net/if_arp.h>
30 #include <sys/ioctl.h>
31 
32 #include <asm/types.h>
33 #include <linux/netlink.h>
34 #include <linux/rtnetlink.h>
35 
36 #include <not-cancel.h>
37 
38 #include "netlinkaccess.h"
39 
40 void
__check_native(uint32_t a1_index,int * a1_native,uint32_t a2_index,int * a2_native)41 __check_native (uint32_t a1_index, int *a1_native,
42 		uint32_t a2_index, int *a2_native)
43 {
44   int fd = __socket (PF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_ROUTE);
45 
46   struct sockaddr_nl nladdr;
47   memset (&nladdr, '\0', sizeof (nladdr));
48   nladdr.nl_family = AF_NETLINK;
49 
50   socklen_t addr_len = sizeof (nladdr);
51   bool use_malloc = false;
52 
53   if (fd < 0)
54     return;
55 
56   if (__bind (fd, (struct sockaddr *) &nladdr, sizeof (nladdr)) != 0
57       || __getsockname (fd, (struct sockaddr *) &nladdr, &addr_len) != 0)
58     goto out;
59 
60   pid_t pid = nladdr.nl_pid;
61   struct req
62   {
63     struct nlmsghdr nlh;
64     struct rtgenmsg g;
65     /* struct rtgenmsg consists of a single byte.  This means there
66        are three bytes of padding included in the REQ definition.
67        We make them explicit here.  */
68     char pad[3];
69   } req;
70 
71   req.nlh.nlmsg_len = sizeof (req);
72   req.nlh.nlmsg_type = RTM_GETLINK;
73   req.nlh.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST;
74   req.nlh.nlmsg_pid = 0;
75   req.nlh.nlmsg_seq = time_now ();
76   req.g.rtgen_family = AF_UNSPEC;
77 
78   assert (sizeof (req) - offsetof (struct req, pad) == 3);
79   memset (req.pad, '\0', sizeof (req.pad));
80 
81   memset (&nladdr, '\0', sizeof (nladdr));
82   nladdr.nl_family = AF_NETLINK;
83 
84 #ifdef PAGE_SIZE
85   /* Help the compiler optimize out the malloc call if PAGE_SIZE
86      is constant and smaller or equal to PTHREAD_STACK_MIN/4.  */
87   const size_t buf_size = PAGE_SIZE;
88 #else
89   const size_t buf_size = __getpagesize ();
90 #endif
91   char *buf;
92 
93   if (__libc_use_alloca (buf_size))
94     buf = alloca (buf_size);
95   else
96     {
97       buf = malloc (buf_size);
98       if (buf != NULL)
99 	use_malloc = true;
100       else
101 	goto out;
102     }
103 
104   struct iovec iov = { buf, buf_size };
105 
106   if (TEMP_FAILURE_RETRY (__sendto (fd, (void *) &req, sizeof (req), 0,
107 				    (struct sockaddr *) &nladdr,
108 				    sizeof (nladdr))) < 0)
109     goto out;
110 
111   bool done = false;
112   do
113     {
114       struct msghdr msg =
115 	{
116 	  .msg_name = (void *) &nladdr,
117 	  .msg_namelen =  sizeof (nladdr),
118 	  .msg_iov = &iov,
119 	  .msg_iovlen = 1,
120 	  .msg_control = NULL,
121 	  .msg_controllen = 0,
122 	  .msg_flags = 0
123 	};
124 
125       ssize_t read_len = TEMP_FAILURE_RETRY (__recvmsg (fd, &msg, 0));
126       __netlink_assert_response (fd, read_len);
127       if (read_len < 0)
128 	goto out;
129 
130       if (msg.msg_flags & MSG_TRUNC)
131 	goto out;
132 
133       struct nlmsghdr *nlmh;
134       for (nlmh = (struct nlmsghdr *) buf;
135 	   NLMSG_OK (nlmh, (size_t) read_len);
136 	   nlmh = (struct nlmsghdr *) NLMSG_NEXT (nlmh, read_len))
137 	{
138 	  if (nladdr.nl_pid != 0 || (pid_t) nlmh->nlmsg_pid != pid
139 	      || nlmh->nlmsg_seq != req.nlh.nlmsg_seq)
140 	    continue;
141 
142 	  if (nlmh->nlmsg_type == RTM_NEWLINK)
143 	    {
144 	      struct ifinfomsg *ifim = (struct ifinfomsg *) NLMSG_DATA (nlmh);
145 	      int native = (ifim->ifi_type != ARPHRD_TUNNEL6
146 			    && ifim->ifi_type != ARPHRD_TUNNEL
147 			    && ifim->ifi_type != ARPHRD_SIT);
148 
149 	      if (a1_index == ifim->ifi_index)
150 		{
151 		  *a1_native = native;
152 		  a1_index = 0xffffffffu;
153 		}
154 	      if (a2_index == ifim->ifi_index)
155 		{
156 		  *a2_native = native;
157 		  a2_index = 0xffffffffu;
158 		}
159 
160 	      if (a1_index == 0xffffffffu
161 		  && a2_index == 0xffffffffu)
162 		goto out;
163 	    }
164 	  else if (nlmh->nlmsg_type == NLMSG_DONE)
165 	    /* We found the end, leave the loop.  */
166 	    done = true;
167 	}
168     }
169   while (! done);
170 
171 out:
172   __close_nocancel_nostatus (fd);
173 
174   if (use_malloc)
175     free (buf);
176 }
177