Line data Source code
1 : #ifndef HEADER_fd_src_disco_net_fd_net_router_h
2 : #define HEADER_fd_src_disco_net_fd_net_router_h
3 :
4 : /* fd_net_router.h provides an internal API for userland routing. */
5 :
6 : #include "../../waltz/ip/fd_fib4.h"
7 : #include "../../waltz/mib/fd_netdev_tbl.h"
8 : #include "../../waltz/neigh/fd_neigh4_map.h"
9 : #include "../netlink/fd_netlink_tile.h" /* neigh4_solicit */
10 : #include "../../util/net/fd_eth.h"
11 : #include "../../util/net/fd_ip4.h"
12 :
13 : #include <linux/if_arp.h> /* ARPHRD_LOOPBACK */
14 :
15 24 : #define FD_NET_ROUTE_FAIL_NO_ROUTE (0U)
16 24 : #define FD_NET_ROUTE_FAIL_ROUTE_TYPE (1U)
17 12 : #define FD_NET_ROUTE_FAIL_MISSING_INTERFACE (2U)
18 6 : #define FD_NET_ROUTE_FAIL_SOURCE_IP (3U)
19 12 : #define FD_NET_ROUTE_FAIL_UNSUPPORTED_INTERFACE (4U)
20 : #define FD_NET_ROUTE_FAIL_CNT (5U)
21 :
22 60 : #define FD_NET_TX_FILL_INVALID (-1)
23 6 : #define FD_NET_TX_FILL_NO_SOURCE ( 0)
24 132 : #define FD_NET_TX_FILL_OK ( 1)
25 :
26 : struct fd_net_router {
27 : /* Route and neighbor tables */
28 : fd_fib4_t fib_local[1];
29 : fd_fib4_t fib_main[1];
30 : fd_neigh4_hmap_t neigh4[1];
31 : fd_netlink_neigh4_solicit_link_t neigh4_solicit[1];
32 :
33 : /* Netdev table */
34 : fd_netdev_tbl_join_t netdev_tbl; /* local copy in scratch */
35 : fd_netdev_tbl_join_t netdev_shared; /* shared seqlock-protected table */
36 :
37 : uint if_virt;
38 : uint bind_address;
39 : uint default_address;
40 :
41 : struct {
42 : ulong tx_route_fail_cnt[ FD_NET_ROUTE_FAIL_CNT ];
43 : ulong tx_neigh_fail_cnt;
44 : } metrics;
45 : };
46 : typedef struct fd_net_router fd_net_router_t;
47 :
48 : struct fd_net_tx_route {
49 : uchar mac_addrs[12];
50 : ushort mtu;
51 : uint src_ip;
52 : uint if_idx;
53 : uint use_loopback;
54 : uint use_gre;
55 : uint gre_outer_src_ip;
56 : uint gre_outer_dst_ip;
57 : };
58 : typedef struct fd_net_tx_route fd_net_tx_route_t;
59 :
60 : FD_PROTOTYPES_BEGIN
61 :
62 : /* fd_net_tx_route resolves the destination interface index, src MAC
63 : address, and dst MAC address. Returns 1 on success, 0 on failure.
64 : On success, writes the complete route result to out. */
65 :
66 : static int
67 : fd_net_tx_route( fd_net_router_t * ctx,
68 : uint dst_ip,
69 144 : fd_net_tx_route_t * out ) {
70 :
71 144 : if( FD_UNLIKELY( !out ) ) return 0;
72 144 : *out = (fd_net_tx_route_t) {0};
73 :
74 : /* Route lookup */
75 :
76 144 : fd_fib4_hop_t hop[2] = {0};
77 144 : hop[0] = fd_fib4_lookup( ctx->fib_local, dst_ip, 0UL );
78 144 : hop[1] = fd_fib4_lookup( ctx->fib_main, dst_ip, 0UL );
79 144 : fd_fib4_hop_t const * next_hop = fd_fib4_hop_or( hop+0, hop+1 );
80 :
81 144 : uint rtype = next_hop->rtype;
82 144 : uint if_idx = next_hop->if_idx;
83 144 : uint ip4_src = next_hop->ip4_src;
84 :
85 144 : if( FD_UNLIKELY( rtype==FD_FIB4_RTYPE_LOCAL ) ) {
86 6 : rtype = FD_FIB4_RTYPE_UNICAST;
87 6 : if_idx = 1;
88 6 : }
89 :
90 144 : if( FD_UNLIKELY( rtype!=FD_FIB4_RTYPE_UNICAST ) ) {
91 24 : uint const reason = fd_uint_if( rtype==FD_FIB4_RTYPE_THROW,
92 24 : FD_NET_ROUTE_FAIL_NO_ROUTE,
93 24 : FD_NET_ROUTE_FAIL_ROUTE_TYPE );
94 24 : ctx->metrics.tx_route_fail_cnt[ reason ]++;
95 24 : return 0;
96 24 : }
97 :
98 120 : fd_netdev_t * netdev = fd_netdev_tbl_query( &ctx->netdev_tbl, if_idx );
99 120 : if( !netdev ) {
100 12 : ctx->metrics.tx_route_fail_cnt[ FD_NET_ROUTE_FAIL_MISSING_INTERFACE ]++;
101 12 : return 0;
102 12 : }
103 :
104 108 : ip4_src = fd_uint_if( !!ctx->bind_address, ctx->bind_address, ip4_src );
105 108 : out->mtu = netdev->mtu;
106 108 : out->src_ip = ip4_src;
107 108 : out->if_idx = if_idx;
108 :
109 108 : if( netdev->dev_type==ARPHRD_LOOPBACK ) {
110 6 : memset( out->mac_addrs, 0, sizeof(out->mac_addrs) );
111 6 : out->src_ip = fd_uint_if( !ip4_src, FD_IP4_ADDR( 127,0,0,1 ), ip4_src );
112 6 : out->use_loopback = 1U;
113 6 : return 1;
114 102 : } else if( netdev->dev_type==ARPHRD_IPGRE ) {
115 : /* skip MAC addrs lookup for GRE inner dst ip */
116 18 : out->gre_outer_src_ip = netdev->gre_src_ip;
117 18 : out->gre_outer_dst_ip = netdev->gre_dst_ip;
118 18 : out->use_gre = 1U;
119 18 : return 1;
120 18 : }
121 :
122 84 : if( FD_UNLIKELY( netdev->dev_type!=ARPHRD_ETHER ) ) {
123 0 : ctx->metrics.tx_route_fail_cnt[ FD_NET_ROUTE_FAIL_UNSUPPORTED_INTERFACE ]++;
124 0 : return 0;
125 0 : }
126 :
127 84 : if( FD_UNLIKELY( if_idx!=ctx->if_virt ) ) {
128 12 : ctx->metrics.tx_route_fail_cnt[ FD_NET_ROUTE_FAIL_UNSUPPORTED_INTERFACE ]++;
129 12 : return 0;
130 12 : }
131 :
132 : /* Neighbor resolve */
133 72 : uint neigh_ip = next_hop->ip4_gw;
134 72 : if( !neigh_ip ) neigh_ip = dst_ip;
135 :
136 72 : fd_neigh4_entry_t neigh[1];
137 72 : int neigh_res = fd_neigh4_hmap_query_entry( ctx->neigh4, neigh_ip, neigh );
138 72 : if( FD_UNLIKELY( neigh_res!=FD_MAP_SUCCESS ) ) {
139 : /* Neighbor not found */
140 12 : fd_netlink_neigh4_solicit( ctx->neigh4_solicit, neigh_ip, if_idx, fd_frag_meta_ts_comp( fd_tickcount() ) );
141 12 : ctx->metrics.tx_neigh_fail_cnt++;
142 12 : return 0;
143 12 : }
144 60 : if( FD_UNLIKELY( neigh->state != FD_NEIGH4_STATE_ACTIVE ) ) {
145 0 : ctx->metrics.tx_neigh_fail_cnt++;
146 0 : return 0;
147 0 : }
148 60 : ip4_src = fd_uint_if( !ip4_src, ctx->default_address, ip4_src );
149 60 : out->src_ip = ip4_src;
150 60 : memcpy( out->mac_addrs+0, neigh->mac_addr, 6 );
151 60 : memcpy( out->mac_addrs+6, netdev->mac_addr, 6 );
152 :
153 60 : return 1;
154 60 : }
155 :
156 : static int
157 : fd_net_tx_validate_ip4( fd_ip4_hdr_t const * ip4_hdr,
158 171 : ulong ip4_buf_sz ) {
159 171 : if( FD_UNLIKELY( !ip4_hdr || ip4_buf_sz<sizeof(fd_ip4_hdr_t) ) ) return 0;
160 171 : ulong const ip4_hdr_sz = FD_IP4_GET_LEN( *ip4_hdr );
161 171 : return FD_IP4_GET_VERSION( *ip4_hdr )==4U &&
162 171 : ip4_hdr_sz>=sizeof(fd_ip4_hdr_t) && ip4_hdr_sz<=ip4_buf_sz;
163 171 : }
164 :
165 : static int
166 : fd_net_tx_validate_frame( void const * frame,
167 96 : ulong frame_sz ) {
168 96 : if( FD_UNLIKELY( !frame ||
169 96 : frame_sz<sizeof(fd_eth_hdr_t)+sizeof(fd_ip4_hdr_t) ||
170 96 : frame_sz>FD_ETH_PAYLOAD_MAX ) ) return 0;
171 96 : fd_eth_hdr_t const * eth_hdr = (fd_eth_hdr_t const *)frame;
172 96 : return eth_hdr->net_type==fd_ushort_bswap( FD_ETH_HDR_TYPE_IP ) &&
173 96 : fd_net_tx_validate_ip4( (fd_ip4_hdr_t const *)(eth_hdr+1), frame_sz-sizeof(fd_eth_hdr_t) );
174 96 : }
175 :
176 : /* fd_net_tx_fill_ip4 validates an IPv4 packet and fills a missing source
177 : address from its selected route. */
178 : static int
179 : fd_net_tx_fill_ip4( fd_net_router_t * ctx,
180 : fd_net_tx_route_t const * route,
181 : fd_ip4_hdr_t * ip4_hdr,
182 75 : ulong ip4_buf_sz ) {
183 75 : if( FD_UNLIKELY( !route || !fd_net_tx_validate_ip4( ip4_hdr, ip4_buf_sz ) ) ) {
184 0 : return FD_NET_TX_FILL_INVALID;
185 0 : }
186 :
187 75 : if( ip4_hdr->saddr==0U ) {
188 75 : if( FD_UNLIKELY( route->src_ip==0U ) ) {
189 6 : ctx->metrics.tx_route_fail_cnt[ FD_NET_ROUTE_FAIL_SOURCE_IP ]++;
190 6 : return FD_NET_TX_FILL_NO_SOURCE;
191 6 : }
192 :
193 69 : ip4_hdr->saddr = route->src_ip;
194 69 : ip4_hdr->check = 0U;
195 69 : ip4_hdr->check = fd_ip4_hdr_check( ip4_hdr );
196 69 : }
197 69 : return FD_NET_TX_FILL_OK;
198 75 : }
199 :
200 : /* fd_net_tx_fill_addrs validates the Ethernet and IPv4 headers, sets both
201 : Ethernet addresses, and fills a missing IPv4 source address. */
202 : static int
203 : fd_net_tx_fill_addrs( fd_net_router_t * ctx,
204 : fd_net_tx_route_t const * route,
205 : uchar * packet,
206 96 : ulong sz ) {
207 96 : if( FD_UNLIKELY( !route || !fd_net_tx_validate_frame( packet, sz ) ) ) {
208 27 : return FD_NET_TX_FILL_INVALID;
209 27 : }
210 :
211 69 : fd_eth_hdr_t * eth_hdr = (fd_eth_hdr_t *)packet;
212 69 : fd_ip4_hdr_t * ip4_hdr = (fd_ip4_hdr_t *)(eth_hdr+1);
213 69 : int const fill_result = fd_net_tx_fill_ip4( ctx, route, ip4_hdr, sz-sizeof(fd_eth_hdr_t) );
214 69 : if( FD_UNLIKELY( fill_result!=FD_NET_TX_FILL_OK ) ) return fill_result;
215 :
216 63 : memcpy( eth_hdr->dst, route->mac_addrs, 12UL );
217 63 : return FD_NET_TX_FILL_OK;
218 69 : }
219 :
220 : FD_PROTOTYPES_END
221 :
222 : #endif /* HEADER_fd_src_disco_net_fd_net_router_h */
|