Skip to content

Commit 4909f00

Browse files
committed
{readme,tc}: remove port check and document configuration with tc
The layer 4 protocol and UDP or TCP port can be distinguished by a tc filter. Document that and remove the logic to discriminate packets by UDP or TCP port from the BPF programme. Add warnings to the README. Signed-off-by: Chester A. Unal <[email protected]>
1 parent e86b8e9 commit 4909f00

File tree

3 files changed

+45
-107
lines changed

3 files changed

+45
-107
lines changed

README.md

Lines changed: 28 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -116,16 +116,40 @@ Load it with `tc` commands:
116116
- Client:
117117
```
118118
tc qdisc add dev "${IFACE}" clsact
119-
tc filter add dev "${IFACE}" egress bpf obj tcp_in_udp_tc.o sec tc_client_egress action csum udp
120-
tc filter add dev "${IFACE}" ingress bpf da obj tcp_in_udp_tc.o sec tc_client_ingress
119+
tc filter add dev "${IFACE}" egress protocol ip flower ip_proto tcp dst_port "${PORT_START}"-"${PORT_END}" action goto chain 1
120+
tc filter add dev "${IFACE}" egress chain 1 bpf object-file tcp_in_udp_tc.o section tc action csum udp
121+
tc filter add dev "${IFACE}" ingress protocol ip flower ip_proto udp src_port "${PORT_START}"-"${PORT_END}" action goto chain 1
122+
tc filter add dev "${IFACE}" ingress chain 1 bpf object-file tcp_in_udp_tc.o section tc direct-action
121123
```
122124
- Server:
123125
```
124126
tc qdisc add dev "${IFACE}" clsact
125-
tc filter add dev "${IFACE}" egress bpf obj tcp_in_udp_tc.o sec tc_server_egress action csum udp
126-
tc filter add dev "${IFACE}" ingress bpf da obj tcp_in_udp_tc.o sec tc_server_ingress
127+
tc filter add dev "${IFACE}" egress protocol ip flower ip_proto tcp src_port "${PORT_START}"-"${PORT_END}" action goto chain 1
128+
tc filter add dev "${IFACE}" egress chain 1 bpf object-file tcp_in_udp_tc.o section tc action csum udp
129+
tc filter add dev "${IFACE}" ingress protocol ip flower ip_proto udp dst_port "${PORT_START}"-"${PORT_END}" action goto chain 1
130+
tc filter add dev "${IFACE}" ingress chain 1 bpf object-file tcp_in_udp_tc.o section tc direct-action
127131
```
128132

133+
If the TCP programme supports setting marks (SO_MARK), use it for egress to
134+
prevent processing traffic that is not from the TCP programme. For client, this
135+
allows traffic to a different IP address with the same TCP port. For server,
136+
this prevents sending packet to BPF programme if the interface has multiple IP
137+
addresses assigned and if the TCP programme doesn't bind to all of them.
138+
139+
- Client & Server:
140+
```
141+
tc filter add dev "${IFACE}" egress handle 2 fw action goto chain 1
142+
```
143+
144+
Be warned that SO_MARK can't be used for ingress as the system doesn't expect
145+
incoming UDP packets. Therefore, all incoming UDP packets from the interface
146+
with matching port will be sent to the BPF programme. For example, if client or
147+
server has UDP traffic to a matching port, incoming packet will be
148+
unintentionally processed by the BPF programme. Therefore, you're recommended to
149+
use ports that are outside of the ephemeral port range set on
150+
net.ipv4.ip_local_port_range (default: 32768-60999). The
151+
net.ipv4.ip_local_port_range option applies to IPv6 too.
152+
129153
GRO/TSO cannot be used on this interface, because each UDP packet will carry a
130154
part of the TCP headers as part of the data: this is specific to one packet, and
131155
it cannot be merged with the next data. Please use this:
@@ -163,15 +187,3 @@ tc filter del dev "${IFACE}" ingress
163187
Because the packets will be in UDP and not TCP, any MSS clamping will have no
164188
effects here. It is important to avoid IP fragmentation. In other words, it
165189
might be required to adapt the MTU (or the MSS).
166-
167-
## Identification
168-
169-
### Client side:
170-
171-
- Ingress: From a specific destination IP and port in UDP
172-
- Egress: To a specific destination IP and port in TCP
173-
174-
### Server side:
175-
176-
- Ingress: To a specific destination IP and port in UDP
177-
- Egress: From a previously used `sk`: use ConnMark to set a specific `SO_MARK`

tcp_in_udp_tc.c

Lines changed: 7 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,6 @@ struct hdr_cursor {
2626
void *pos;
2727
};
2828

29-
__u16 PORT = 5201;
30-
31-
enum side {
32-
SERVER,
33-
CLIENT,
34-
};
35-
3629
/*******************************************
3730
** parse_*hdr helpers from XDP tutorials **
3831
*******************************************/
@@ -156,7 +149,7 @@ static __always_inline int parse_udphdr(struct hdr_cursor *nh,
156149

157150
static __always_inline void
158151
udp_to_tcp(struct __sk_buff *skb, struct hdr_cursor *nh,
159-
struct iphdr *iphdr, struct ipv6hdr *ipv6hdr, enum side side)
152+
struct iphdr *iphdr, struct ipv6hdr *ipv6hdr)
160153
{
161154
void *data_end = (void *)(long)skb->data_end;
162155
void *data = (void *)(long)skb->data;
@@ -169,17 +162,6 @@ udp_to_tcp(struct __sk_buff *skb, struct hdr_cursor *nh,
169162
if (parse_udphdr(nh, data_end, (struct udphdr**)&tuhdr) < 0)
170163
goto out;
171164

172-
switch (side) {
173-
case SERVER:
174-
if (tuhdr->udphdr.dest != bpf_htons(PORT))
175-
goto out;
176-
break;
177-
case CLIENT:
178-
if (tuhdr->udphdr.source != bpf_htons(PORT))
179-
goto out;
180-
break;
181-
}
182-
183165
if (skb->gso_segs > 1) {
184166
bpf_printk("udp-tcp: WARNING, GRO/LRO should be disabled: length:%u, segs:%u, size:%u\n",
185167
skb->len, skb->gso_segs, skb->gso_size);
@@ -249,53 +231,14 @@ udp_to_tcp(struct __sk_buff *skb, struct hdr_cursor *nh,
249231
return;
250232
}
251233

252-
static __always_inline int
253-
tc_ingress(struct __sk_buff *skb, enum side side)
254-
{
255-
void *data_end = (void *)(long)skb->data_end;
256-
void *data = (void *)(long)skb->data;
257-
struct hdr_cursor nh = { .pos = data };
258-
int eth_type, ip_type, ret = TC_ACT_OK;
259-
struct ipv6hdr *ipv6hdr = NULL;
260-
struct iphdr *iphdr = NULL;
261-
struct ethhdr *eth;
262-
263-
eth_type = parse_ethhdr(&nh, data_end, &eth);
264-
if (eth_type == bpf_htons(ETH_P_IP)) {
265-
ip_type = parse_iphdr(&nh, data_end, &iphdr);
266-
} else if (eth_type == bpf_htons(ETH_P_IPV6)) {
267-
ip_type = parse_ip6hdr(&nh, data_end, &ipv6hdr);
268-
} else {
269-
goto out;
270-
}
271-
272-
if (ip_type == IPPROTO_UDP)
273-
udp_to_tcp(skb, &nh, iphdr, ipv6hdr, side);
274-
275-
out:
276-
return ret;
277-
}
278-
279-
SEC("tc_client_ingress")
280-
int client_ingress(struct __sk_buff *skb)
281-
{
282-
return tc_ingress(skb, CLIENT);
283-
}
284-
285-
SEC("tc_server_ingress")
286-
int server_ingress(struct __sk_buff *skb)
287-
{
288-
return tc_ingress(skb, SERVER);
289-
}
290-
291234

292235
/************
293236
** Egress **
294237
************/
295238

296239
static __always_inline int
297240
tcp_to_udp(struct __sk_buff *skb, struct hdr_cursor *nh,
298-
struct iphdr *iphdr, struct ipv6hdr *ipv6hdr, enum side side)
241+
struct iphdr *iphdr, struct ipv6hdr *ipv6hdr)
299242
{
300243
void *data_end = (void *)(long)skb->data_end;
301244
void *data = (void *)(long)skb->data;
@@ -309,17 +252,6 @@ tcp_to_udp(struct __sk_buff *skb, struct hdr_cursor *nh,
309252
if (parse_tcphdr(nh, data_end, &tcphdr) < 0)
310253
goto out;
311254

312-
switch (side) {
313-
case SERVER:
314-
if (tcphdr->source != bpf_htons(PORT))
315-
goto out;
316-
break;
317-
case CLIENT:
318-
if (tcphdr->dest != bpf_htons(PORT))
319-
goto out;
320-
break;
321-
}
322-
323255
if (tcphdr->urg) {
324256
if (iphdr)
325257
bpf_printk("tcp-udp: Skip: %pI4:%u -> %pI4:%u: urgent\n",
@@ -386,8 +318,8 @@ tcp_to_udp(struct __sk_buff *skb, struct hdr_cursor *nh,
386318
return TC_ACT_OK;
387319
}
388320

389-
static __always_inline int
390-
tc_egress(struct __sk_buff *skb, enum side side)
321+
SEC("tc")
322+
int tc_tcp_in_udp(struct __sk_buff *skb)
391323
{
392324
void *data_end = (void *)(long)skb->data_end;
393325
void *data = (void *)(long)skb->data;
@@ -407,22 +339,12 @@ tc_egress(struct __sk_buff *skb, enum side side)
407339
}
408340

409341
if (ip_type == IPPROTO_TCP)
410-
return tcp_to_udp(skb, &nh, iphdr, ipv6hdr, side);
342+
return tcp_to_udp(skb, &nh, iphdr, ipv6hdr);
343+
else if (ip_type == IPPROTO_UDP)
344+
udp_to_tcp(skb, &nh, iphdr, ipv6hdr);
411345

412346
out:
413347
return ret;
414348
}
415349

416-
SEC("tc_client_egress")
417-
int client_egress(struct __sk_buff *skb)
418-
{
419-
return tc_egress(skb, CLIENT);
420-
}
421-
422-
SEC("tc_server_egress")
423-
int server_egress(struct __sk_buff *skb)
424-
{
425-
return tc_egress(skb, SERVER);
426-
}
427-
428350
char _license[] SEC("license") = "GPL";

test.sh

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,14 +40,16 @@ server()
4040

4141
tc_client()
4242
{
43-
local ns="${NS}_cpe" iface="int"
43+
local ns="${NS}_cpe" iface="int" port_start="31001" port_end="31003"
4444

4545
# ip netns will umount everything on exit
4646
ip netns exec "${ns}" sh -c "mount -t debugfs none /sys/kernel/debug && cat /sys/kernel/debug/tracing/trace_pipe" &
4747

4848
tc -n "${ns}" qdisc add dev "${iface}" clsact
49-
tc -n "${ns}" filter add dev "${iface}" egress bpf obj tcp_in_udp_tc.o sec tc_client_egress action csum udp index 100
50-
tc -n "${ns}" filter add dev "${iface}" ingress bpf da obj tcp_in_udp_tc.o sec tc_client_ingress
49+
tc -n "${ns}" filter add dev "${iface}" egress protocol ip flower ip_proto tcp dst_port "${port_start}"-"${port_end}" action goto chain 1
50+
tc -n "${ns}" filter add dev "${iface}" egress chain 1 bpf object-file tcp_in_udp_tc.o section tc action csum udp
51+
tc -n "${ns}" filter add dev "${iface}" ingress protocol ip flower ip_proto udp src_port "${port_start}"-"${port_end}" action goto chain 1
52+
tc -n "${ns}" filter add dev "${iface}" ingress chain 1 bpf object-file tcp_in_udp_tc.o section tc direct-action
5153

5254
tc -n "${ns}" filter show dev "${iface}" egress
5355
tc -n "${ns}" filter show dev "${iface}" ingress
@@ -59,14 +61,16 @@ tc_client()
5961

6062
tc_server()
6163
{
62-
local ns="${NS}_net" iface="int"
64+
local ns="${NS}_net" iface="int" port_start="31001" port_end="31003"
6365

6466
# ip netns will umount everything on exit
6567
ip netns exec "${ns}" sh -c "mount -t debugfs none /sys/kernel/debug && cat /sys/kernel/debug/tracing/trace_pipe" &
6668

6769
tc -n "${ns}" qdisc add dev "${iface}" clsact
68-
tc -n "${ns}" filter add dev "${iface}" egress bpf obj tcp_in_udp_tc.o sec tc_server_egress action csum udp index 100
69-
tc -n "${ns}" filter add dev "${iface}" ingress bpf da obj tcp_in_udp_tc.o sec tc_server_ingress
70+
tc -n "${ns}" filter add dev "${iface}" egress protocol ip flower ip_proto tcp src_port "${port_start}"-"${port_end}" action goto chain 1
71+
tc -n "${ns}" filter add dev "${iface}" egress chain 1 bpf object-file tcp_in_udp_tc.o section tc action csum udp
72+
tc -n "${ns}" filter add dev "${iface}" ingress protocol ip flower ip_proto udp dst_port "${port_start}"-"${port_end}" action goto chain 1
73+
tc -n "${ns}" filter add dev "${iface}" ingress chain 1 bpf object-file tcp_in_udp_tc.o section tc direct-action
7074

7175
tc -n "${ns}" filter show dev "${iface}" egress
7276
tc -n "${ns}" filter show dev "${iface}" ingress

0 commit comments

Comments
 (0)