Line data Source code
1 : #include "fd_netdev_netlink.h"
2 : #include "../../util/fd_util.h"
3 : #include "fd_netdev_tbl.h"
4 :
5 : #if !defined(__linux__)
6 : #error "fd_fib4_netlink.c requires a Linux system with kernel headers"
7 : #endif
8 :
9 : #include <errno.h>
10 : #include <linux/if.h> /* IFNAMSIZ */
11 : #include <linux/if_arp.h> /* ARPHRD_NETROM */
12 : #include <linux/rtnetlink.h> /* RTM_{...}, NLM_{...} */
13 :
14 : static fd_netdev_t *
15 0 : fd_netdev_init( fd_netdev_t * netdev ) {
16 0 : *netdev = (fd_netdev_t) {
17 0 : .mtu = 1500,
18 0 : .if_idx = 0,
19 0 : .slave_tbl_idx = -1,
20 0 : .master_idx = -1,
21 0 : .oper_status = FD_OPER_STATUS_INVALID
22 0 : };
23 0 : return netdev;
24 0 : }
25 :
26 : FD_FN_CONST static uchar
27 0 : ifoper_to_oper_status( uint if_oper ) {
28 : /* Linux uses different enum values than RFC 2863 */
29 0 : switch( if_oper ) {
30 0 : case IF_OPER_UNKNOWN:
31 0 : return FD_OPER_STATUS_UNKNOWN;
32 0 : case IF_OPER_NOTPRESENT:
33 0 : return FD_OPER_STATUS_NOT_PRESENT;
34 0 : case IF_OPER_DOWN:
35 0 : return FD_OPER_STATUS_DOWN;
36 0 : case IF_OPER_LOWERLAYERDOWN:
37 0 : return FD_OPER_STATUS_LOWER_LAYER_DOWN;
38 0 : case IF_OPER_TESTING:
39 0 : return FD_OPER_STATUS_TESTING;
40 0 : case IF_OPER_DORMANT:
41 0 : return FD_OPER_STATUS_DORMANT;
42 0 : case IF_OPER_UP:
43 0 : return FD_OPER_STATUS_UP;
44 0 : default:
45 0 : return FD_OPER_STATUS_INVALID;
46 0 : }
47 0 : }
48 :
49 : int
50 : fd_netdev_netlink_load_table( fd_netdev_tbl_join_t * tbl,
51 0 : fd_netlink_t * netlink ) {
52 :
53 0 : fd_netdev_tbl_reset( tbl );
54 :
55 0 : uint seq = netlink->seq++;
56 :
57 0 : struct {
58 0 : struct nlmsghdr nlh;
59 0 : struct ifinfomsg ifi;
60 0 : } request;
61 0 : request.nlh = (struct nlmsghdr) {
62 0 : .nlmsg_type = RTM_GETLINK,
63 0 : .nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP,
64 0 : .nlmsg_len = sizeof(request),
65 0 : .nlmsg_seq = seq
66 0 : };
67 0 : request.ifi = (struct ifinfomsg) {
68 0 : .ifi_family = AF_PACKET,
69 0 : .ifi_type = ARPHRD_NETROM,
70 0 : };
71 :
72 0 : long send_res = sendto( netlink->fd, &request, sizeof(request), 0, NULL, 0 );
73 0 : if( FD_UNLIKELY( send_res<0 ) ) {
74 0 : FD_LOG_WARNING(( "netlink send(%d,RTM_GETLINK,NLM_F_REQUEST|NLM_F_DUMP) failed (%i-%s)", netlink->fd, errno, fd_io_strerror( errno ) ));
75 0 : return errno;
76 0 : }
77 0 : if( FD_UNLIKELY( send_res!=sizeof(request) ) ) {
78 0 : FD_LOG_WARNING(( "netlink send(%d,RTM_GETLINK,NLM_F_REQUEST|NLM_F_DUMP) failed (short write)", netlink->fd ));
79 0 : return EPIPE;
80 0 : }
81 :
82 0 : int err = 0;
83 :
84 0 : uchar buf[ 4096 ];
85 0 : fd_netlink_iter_t iter[1];
86 0 : for( fd_netlink_iter_init( iter, netlink, buf, sizeof(buf) );
87 0 : !fd_netlink_iter_done( iter );
88 0 : fd_netlink_iter_next( iter, netlink ) ) {
89 0 : struct nlmsghdr const * nlh = fd_netlink_iter_msg( iter );
90 0 : if( FD_UNLIKELY( nlh->nlmsg_type==NLMSG_ERROR ) ) {
91 0 : struct nlmsgerr * err = NLMSG_DATA( nlh );
92 0 : int nl_err = -err->error;
93 0 : FD_LOG_WARNING(( "netlink RTM_GETLINK,NLM_F_REQUEST|NLM_F_DUMP failed (%d-%s)", nl_err, fd_io_strerror( nl_err ) ));
94 0 : return nl_err;
95 0 : }
96 0 : if( FD_UNLIKELY( nlh->nlmsg_type!=RTM_NEWLINK ) ) {
97 0 : FD_LOG_DEBUG(( "unexpected nlmsg_type %u", nlh->nlmsg_type ));
98 0 : continue;
99 0 : }
100 0 : struct ifinfomsg const * ifi = NLMSG_DATA( nlh );
101 :
102 0 : if( FD_UNLIKELY( ifi->ifi_index<0 || ifi->ifi_index>=tbl->hdr->dev_max ) ) {
103 0 : FD_LOG_WARNING(( "Error reading interface table: interface %d is beyond max of %u", ifi->ifi_index, tbl->hdr->dev_max ));
104 0 : err = ENOSPC;
105 0 : break;
106 0 : }
107 0 : if( ifi->ifi_type!=ARPHRD_ETHER && ifi->ifi_type!=ARPHRD_LOOPBACK ) continue;
108 :
109 0 : struct ifinfomsg * msg = NLMSG_DATA( nlh );
110 0 : struct rtattr * rat = (void *)( (ulong)msg + NLMSG_ALIGN( sizeof(struct ifinfomsg) ) );
111 0 : long rat_sz = (long)nlh->nlmsg_len - (long)NLMSG_ALIGN( sizeof(struct ifinfomsg) );
112 :
113 0 : fd_netdev_t netdev[1];
114 0 : fd_netdev_init( netdev );
115 :
116 0 : for( ; RTA_OK( rat, rat_sz ); rat=RTA_NEXT( rat, rat_sz ) ) {
117 0 : void * rta = RTA_DATA( rat );
118 0 : ulong rta_sz = RTA_PAYLOAD( rat );
119 :
120 0 : switch( rat->rta_type ) {
121 :
122 0 : case IFLA_IFNAME:
123 : /* Includes trailing zero */
124 0 : if( FD_UNLIKELY( rta_sz==0 || rta_sz>IFNAMSIZ ) ) {
125 0 : FD_LOG_WARNING(( "Error reading interface table: IFLA_IFNAME has unsupported size %lu", rta_sz ));
126 0 : err = EPROTO;
127 0 : goto fail;
128 0 : }
129 0 : memcpy( netdev->name, rta, rta_sz );
130 0 : netdev->name[ rta_sz-1 ] = '\0';
131 0 : break;
132 :
133 0 : case IFLA_ADDRESS:
134 0 : if( FD_UNLIKELY( rta_sz==6UL ) ) {
135 0 : memcpy( netdev->mac_addr, rta, 6 );
136 0 : }
137 0 : break;
138 :
139 0 : case IFLA_OPERSTATE:
140 0 : if( FD_UNLIKELY( rta_sz!=1UL ) ) {
141 0 : FD_LOG_WARNING(( "Error reading interface table: IFLA_OPERSTATE has unexpected size %lu", rta_sz ));
142 0 : err = EPROTO;
143 0 : goto fail;
144 0 : }
145 0 : netdev->oper_status = (uchar)ifoper_to_oper_status( FD_LOAD( uchar, rta ) );
146 0 : break;
147 :
148 0 : case IFLA_MTU:
149 0 : if( FD_UNLIKELY( rta_sz!=4UL ) ) {
150 0 : FD_LOG_WARNING(( "Error reading interface table: IFLA_MTU has unexpected size %lu", rta_sz ));
151 0 : err = EPROTO;
152 0 : goto fail;
153 0 : }
154 0 : netdev->mtu = (ushort)fd_uint_min( FD_LOAD( uint, rta ), USHORT_MAX );
155 0 : break;
156 :
157 0 : case IFLA_MASTER: {
158 0 : if( FD_UNLIKELY( rta_sz!=4UL ) ) {
159 0 : FD_LOG_WARNING(( "Error reading interface table: IFLA_MASTER has unexpected size %lu", rta_sz ));
160 0 : err = EPROTO;
161 0 : goto fail;
162 0 : }
163 0 : int master_idx = FD_LOAD( int, rta );
164 0 : if( FD_UNLIKELY( master_idx<0 || master_idx>=tbl->hdr->dev_max ) ) {
165 0 : FD_LOG_WARNING(( "Error reading interface table: IFLA_MASTER has invalid index %d", master_idx ));
166 0 : err = EPROTO;
167 0 : goto fail;
168 0 : }
169 0 : netdev->master_idx = (short)master_idx;
170 0 : break;
171 0 : }
172 :
173 0 : } /* switch( rat->rta_type ) */
174 0 : } /* for each RTA */
175 :
176 0 : if( ifi->ifi_type==ARPHRD_LOOPBACK ) {
177 0 : netdev->oper_status = FD_OPER_STATUS_UP;
178 0 : }
179 :
180 0 : tbl->dev_tbl[ ifi->ifi_index ] = *netdev;
181 0 : tbl->hdr->dev_cnt = (ushort)fd_uint_max( tbl->hdr->dev_cnt, (uint)ifi->ifi_index+1U );
182 0 : }
183 :
184 : /* Walk the table again to index the bond master => slave mapping */
185 :
186 0 : for( ulong j=0UL; j<(tbl->hdr->dev_cnt); j++ ) {
187 : /* Only consider UP slaves */
188 0 : if( tbl->dev_tbl[ j ].oper_status!=FD_OPER_STATUS_UP ) continue;
189 :
190 : /* Find master */
191 0 : int master_idx = tbl->dev_tbl[ j ].master_idx;
192 0 : if( master_idx<0 ) continue;
193 0 : if( FD_UNLIKELY( master_idx>=tbl->hdr->dev_max ) ) continue; /* unreachable */
194 0 : fd_netdev_t * master = &tbl->dev_tbl[ master_idx ];
195 :
196 : /* Allocate a new bond slave table if needed */
197 0 : if( master->slave_tbl_idx<0 ) {
198 0 : if( FD_UNLIKELY( tbl->hdr->bond_cnt>=tbl->hdr->bond_max ) ) {
199 0 : FD_LOG_WARNING(( "Error reading interface table: Found %u bond devices but max is %u", tbl->hdr->bond_cnt, tbl->hdr->bond_max ));
200 0 : continue;
201 0 : }
202 :
203 0 : master->slave_tbl_idx = (short)tbl->hdr->bond_cnt;
204 0 : tbl->hdr->bond_cnt = (ushort)( tbl->hdr->bond_cnt+1U );
205 : /* Assume that this table is empty */
206 0 : }
207 :
208 0 : fd_netdev_bond_t * bond = &tbl->bond_tbl[ master->slave_tbl_idx ];
209 0 : if( FD_UNLIKELY( bond->slave_cnt>=FD_NETDEV_BOND_SLAVE_MAX ) ) {
210 0 : FD_LOG_WARNING(( "Error reading interface table: Bond device %d has %u slaves but max is %d", master_idx, bond->slave_cnt, FD_NETDEV_BOND_SLAVE_MAX ));
211 0 : continue;
212 0 : }
213 0 : bond->slave_idx[ bond->slave_cnt ] = (ushort)j;
214 0 : bond->slave_cnt = (uchar)( bond->slave_cnt+1U );
215 0 : }
216 :
217 0 : return 0;
218 :
219 0 : fail:
220 0 : fd_netlink_iter_drain( iter, netlink );
221 0 : return err;
222 0 : }
|