For my project, I want to insert a timestamp option into outer IP header of a GENEVE encapsulated packet. I tried with a BPF program which attached into egress TC hook of a GENEVE vport of OVS. My BPF program is below but it inserts option field into inner ip header. So, I have 2 questions:
- Can use BPF to insert IP option into IP outer header?
- If it is possible, How to do it?
#include <linux/pkt_cls.h>
#include <linux/if_ether.h>
#include <linux/ip.h>
#include <arpa/inet.h>
#ifndef __section
# define __section(NAME) \
__attribute__((section(NAME), used))
#endif
#ifndef __inline
# define __inline \
inline __attribute__((always_inline))
#endif
#ifndef lock_xadd
# define lock_xadd(ptr, val) \
((void)__sync_fetch_and_add(ptr, val))
#endif
#ifndef BPF_FUNC
# define BPF_FUNC(NAME, ...) \
(*NAME)(__VA_ARGS__) = (void *)BPF_FUNC_##NAME
#endif
#define OPT_LEN 4
#define IP_HDL 20
static int BPF_FUNC(skb_adjust_room, struct __sk_buff *skb, __s32 len_diff, __u32 mode, __u64 flags);
//static int BPF_FUNC(skb_vlan_push, struct __sk_buff *skb, uint16_t proto, uint16_t vlan_tci);
__section("egress")
int push_tun_opt(struct __sk_buff *skb) {
struct ethhdr *eth;
struct iphdr *iph;
__be32 *opt;
volatile void *data, *data_end;
int ret = TC_ACT_OK;
//skb_vlan_push(skb,0x8100,0x1);
skb_adjust_room(skb, size(OPT_LEN), BPF_ADJ_ROOM_NET, BPF_F_ADJ_ROOM_ENCAP_L3_IPV4);
data = (void *)(long)skb->data;
data_end = (void *)(long)skb->data_end;
eth = (void *)data;
iph = (void *)(eth+1);
opt = (void *)(iph+1);
if ((void *)(opt+1) > data_end)
goto out;
*opt = 0x1234abcd;
out:
return ret;
}
char __license[] __section("license") = "GPL";
BPF program with none flags
#include <linux/pkt_cls.h>
#include <linux/if_ether.h>
#include <linux/ip.h>
#include <arpa/inet.h>
#include <uapi/linux/bpf.h>
#ifndef __section
# define __section(NAME) \
__attribute__((section(NAME), used))
#endif
#ifndef __inline
# define __inline \
inline __attribute__((always_inline))
#endif
#ifndef lock_xadd
# define lock_xadd(ptr, val) \
((void)__sync_fetch_and_add(ptr, val))
#endif
#ifndef BPF_FUNC
# define BPF_FUNC(NAME, ...) \
(*NAME)(__VA_ARGS__) = (void *)BPF_FUNC_##NAME
#endif
#define OPT_LEN 4
#define IP_HDL 20
static int BPF_FUNC(skb_adjust_room, struct __sk_buff *skb, __s32 len_diff, __u32 mode, __u64 flags);
//static int BPF_FUNC(skb_vlan_push, struct __sk_buff *skb, uint16_t proto, uint16_t vlan_tci);
static int BPF_FUNC(skb_store_bytes, struct __sk_buff *skb, __s32 offset, const void *from, __u32 len, __u64 flags);
__section("egress")
int push_tun_opt(struct __sk_buff *skb) {
struct ethhdr *eth;
struct iphdr *iph;
__be32 *opt;
volatile void *data, *data_end;
int ret = TC_ACT_OK;
//skb_vlan_push(skb,0x8100,0x1);
skb_adjust_room(skb, OPT_LEN, BPF_ADJ_ROOM_NET, 0);
data = (void *)(long)skb->data;
data_end = (void *)(long)skb->data_end;
eth = (void *)data;
iph = (void *)(eth+1);
opt = (void *)(iph+1);
if ((void *)(opt+1) > data_end)
goto out;
*opt = 0x1234abcd;
//skb_store_bytes(skb, sizeof(struct ethhdr) + sizeof(struct iphdr), &opt, OPT_LEN, BPF_F_RECOMPUTE_CSUM);
out:
return ret;
}
char __license[] __section("license") = "GPL";
geneve interface Edit 1: I added 2 images: a ping packet is encapsulated in GENEVE tunnel before and after using tc-bpf program.
Edit 2: I added BPF program with 0 flags.