Line data Source code
1 : #include <sys/types.h>
2 : #include <sys/socket.h>
3 : #include <linux/netlink.h>
4 : #include <linux/rtnetlink.h>
5 : #include <errno.h>
6 : #include <unistd.h>
7 :
8 : #include "fd_netlink1.h"
9 : #include "../../util/fd_util.h"
10 :
11 : FD_TL ulong fd_netlink_enobufs_cnt;
12 :
13 : static int
14 3 : fd_nl_create_socket( void ) {
15 3 : int fd = socket( AF_NETLINK, SOCK_RAW, NETLINK_ROUTE );
16 :
17 3 : if( FD_UNLIKELY( fd<0 ) ) {
18 0 : FD_LOG_WARNING(( "socket(AF_NETLINK,SOCK_RAW,NETLINK_ROUTE) failed (%i-%s)",
19 0 : errno, fd_io_strerror( errno ) ));
20 0 : return -1;
21 0 : }
22 :
23 3 : int one = 1;
24 3 : if( setsockopt( fd, SOL_NETLINK, NETLINK_EXT_ACK, &one, sizeof(one) )<0 ) {
25 0 : FD_LOG_WARNING(( "setsockopt(sock,SOL_NETLINK,NETLINK_EXT_ACK) failed (%i-%s)",
26 0 : errno, fd_io_strerror( errno ) ));
27 0 : close( fd );
28 0 : return -1;
29 0 : }
30 :
31 3 : return fd;
32 3 : }
33 :
34 : static void
35 3 : fd_nl_close_socket( int fd ) {
36 3 : if( fd >= 0 ) {
37 3 : close( fd );
38 3 : }
39 3 : }
40 :
41 : long
42 : fd_netlink_read_socket( int fd,
43 : uchar * buf,
44 12 : ulong buf_sz ) {
45 : /* netlink is datagram based
46 : once a recv succeeds, any un-received bytes are lost
47 : and the next datagram will be properly aligned in the buffer */
48 12 : for(;;) {
49 12 : long len = recvfrom( fd, buf, buf_sz, 0, NULL, NULL );
50 12 : if( FD_UNLIKELY( len<=0L ) ) {
51 0 : if( len==0L ) continue;
52 0 : if( errno==EINTR ) continue;
53 0 : if( errno==ENOBUFS ) {
54 0 : fd_netlink_enobufs_cnt++;
55 0 : continue;
56 0 : }
57 0 : FD_LOG_WARNING(( "netlink recv failed (%d-%s)", errno, fd_io_strerror( errno ) ));
58 0 : return -(long)errno;
59 0 : }
60 12 : return len;
61 12 : }
62 12 : }
63 :
64 : fd_netlink_t *
65 : fd_netlink_init( fd_netlink_t * nl,
66 3 : uint seq0 ) {
67 3 : nl->fd = fd_nl_create_socket();
68 3 : if( FD_UNLIKELY( nl->fd<0 ) ) return NULL;
69 3 : nl->seq = seq0;
70 3 : return nl;
71 3 : }
72 :
73 : void *
74 3 : fd_netlink_fini( fd_netlink_t * nl ) {
75 3 : fd_nl_close_socket( nl->fd );
76 3 : nl->fd = -1;
77 3 : return nl;
78 3 : }
79 :
80 : static void
81 : fd_netlink_iter_recvmsg( fd_netlink_iter_t * iter,
82 12 : fd_netlink_t * netlink ) {
83 12 : long len = fd_netlink_read_socket( netlink->fd, iter->buf, iter->buf_sz );
84 12 : if( len<0L ) {
85 0 : iter->err = (int)-len;
86 0 : return;
87 0 : }
88 12 : iter->msg0 = iter->buf;
89 12 : iter->msg1 = iter->buf+len;
90 12 : }
91 :
92 : /* fd_netlink_iter_verify_next bounds checks the next message. If out-of-
93 : bounds, logs warning and sets error EPROTO. This prevents the iterator
94 : from returning an out-of-bounds netlink message. */
95 :
96 : static void
97 84 : fd_netlink_iter_bounds_check( fd_netlink_iter_t * iter ) {
98 84 : if( fd_netlink_iter_done( iter ) ) return;
99 :
100 78 : struct nlmsghdr const * nlh = fd_type_pun_const( iter->msg0 );
101 78 : if( FD_UNLIKELY( iter->msg0 + sizeof(struct nlmsghdr) > iter->msg1 ) ) {
102 0 : FD_LOG_WARNING(( "netlink message header out-of-bounds" ));
103 0 : iter->err = EPROTO;
104 0 : return;
105 0 : }
106 78 : if( FD_UNLIKELY( nlh->nlmsg_len < sizeof(struct nlmsghdr) ) ) {
107 : /* prevent infinite loop */
108 0 : FD_LOG_WARNING(( "netlink message smaller than header" ));
109 0 : iter->err = EPROTO;
110 0 : return;
111 0 : }
112 78 : if( FD_UNLIKELY( iter->msg0 + nlh->nlmsg_len > iter->msg1 ) ) {
113 0 : FD_LOG_WARNING(( "netlink message out-of-bounds: cur=[%p,%p) buf=[%p,%p)",
114 0 : (void *)iter->msg0, (void *)iter->msg1, (void *)iter->buf, (void *)( iter->buf+iter->buf_sz ) ));
115 0 : iter->err = EPROTO;
116 0 : return;
117 0 : }
118 78 : }
119 :
120 : fd_netlink_iter_t *
121 : fd_netlink_iter_init( fd_netlink_iter_t * iter,
122 : fd_netlink_t * netlink,
123 : uchar * buf,
124 6 : ulong buf_sz ) {
125 6 : *iter = (fd_netlink_iter_t) {
126 6 : .buf = buf,
127 6 : .buf_sz = buf_sz,
128 6 : .msg0 = buf,
129 6 : .msg1 = buf,
130 6 : };
131 :
132 6 : fd_netlink_iter_recvmsg( iter, netlink );
133 6 : fd_netlink_iter_bounds_check( iter );
134 :
135 6 : return iter;
136 6 : }
137 :
138 : int
139 252 : fd_netlink_iter_done( fd_netlink_iter_t const * iter ) {
140 252 : if( (iter->err!=0) | ( iter->msg1 - iter->msg0 < (long)sizeof(struct nlmsghdr) ) ) {
141 0 : return 1;
142 0 : }
143 252 : struct nlmsghdr const * nlh = fd_type_pun_const( iter->msg0 );
144 252 : return nlh->nlmsg_type==NLMSG_DONE;
145 252 : }
146 :
147 : fd_netlink_iter_t *
148 : fd_netlink_iter_next( fd_netlink_iter_t * iter,
149 78 : fd_netlink_t * netlink ) {
150 :
151 78 : if( fd_netlink_iter_done( iter ) ) return iter;
152 :
153 78 : struct nlmsghdr const * nlh = fd_type_pun_const( iter->msg0 );
154 78 : if( !(nlh->nlmsg_flags & NLM_F_MULTI) ) {
155 : /* Last message was not a multipart message */
156 0 : iter->err = -1; /* eof */
157 0 : return iter;
158 0 : }
159 78 : iter->msg0 += NLMSG_ALIGN( nlh->nlmsg_len );
160 :
161 78 : if( iter->msg0 >= iter->msg1 ) {
162 6 : fd_netlink_iter_recvmsg( iter, netlink );
163 6 : }
164 78 : fd_netlink_iter_bounds_check( iter );
165 :
166 78 : return iter;
167 78 : }
168 :
169 : char const *
170 0 : fd_netlink_rtm_type_str( int rtm_type ) {
171 0 : switch( rtm_type ) {
172 0 : case RTN_UNSPEC: return "unspec";
173 0 : case RTN_UNICAST: return "unicast";
174 0 : case RTN_LOCAL: return "local";
175 0 : case RTN_BROADCAST: return "broadcast";
176 0 : case RTN_ANYCAST: return "anycast";
177 0 : case RTN_MULTICAST: return "multicast";
178 0 : case RTN_BLACKHOLE: return "blackhole";
179 0 : case RTN_UNREACHABLE: return "unreachable";
180 0 : case RTN_PROHIBIT: return "prohibit";
181 0 : case RTN_THROW: return "throw";
182 0 : case RTN_NAT: return "nat";
183 0 : case RTN_XRESOLVE: return "xresolve";
184 0 : default: return "unknown";
185 0 : }
186 0 : }
187 :
188 : char const *
189 0 : fd_netlink_rtattr_str( int rta_type ) {
190 0 : switch( rta_type ) {
191 : /* These exist since at least Linux v3.7 */
192 0 : case RTA_DST: return "dst";
193 0 : case RTA_SRC: return "src";
194 0 : case RTA_IIF: return "iif";
195 0 : case RTA_OIF: return "oif";
196 0 : case RTA_GATEWAY: return "gateway";
197 0 : case RTA_PRIORITY: return "priority";
198 0 : case RTA_PREFSRC: return "prefsrc";
199 0 : case RTA_METRICS: return "metrics";
200 0 : case RTA_MULTIPATH: return "multipath";
201 0 : case RTA_FLOW: return "flow";
202 0 : case RTA_CACHEINFO: return "cacheinfo";
203 0 : case RTA_TABLE: return "table";
204 0 : case RTA_MARK: return "mark";
205 : #ifdef RTA_MFC_STATS
206 : case RTA_MFC_STATS: return "mfc_stats";
207 : #endif
208 : #ifdef RTA_VIA
209 : case RTA_VIA: return "via";
210 : #endif
211 : #ifdef RTA_NEWDST
212 : case RTA_NEWDST: return "newdst";
213 : #endif
214 : #ifdef RTA_PREF
215 : case RTA_PREF: return "pref";
216 : #endif
217 : #ifdef RTA_ENCAP_TYPE
218 : case RTA_ENCAP_TYPE: return "encap_type";
219 : #endif
220 : #ifdef RTA_ENCAP
221 : case RTA_ENCAP: return "encap";
222 : #endif
223 : #ifdef RTA_EXPIRES
224 : case RTA_EXPIRES: return "expires";
225 : #endif
226 : #ifdef RTA_PAD
227 : case RTA_PAD: return "pad";
228 : #endif
229 : #ifdef RTA_UID
230 : case RTA_UID: return "uid";
231 : #endif
232 : #ifdef RTA_TTL_PROPAGATE
233 : case RTA_TTL_PROPAGATE: return "ttl_propagate";
234 : #endif
235 : #ifdef RTA_IP_PROTO
236 : case RTA_IP_PROTO: return "ip_proto";
237 : #endif
238 : #ifdef RTA_SPORT
239 : case RTA_SPORT: return "sport";
240 : #endif
241 : #ifdef RTA_DPORT
242 : case RTA_DPORT: return "dport";
243 : #endif
244 : #ifdef RTA_NH_ID
245 : case RTA_NH_ID: return "nh_id";
246 : #endif
247 0 : default: return "unknown";
248 0 : }
249 0 : }
|