2018-04-18 07:42:23 +03:00
/* SPDX-License-Identifier: GPL-2.0
* Copyright ( c ) 2018 Facebook
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of version 2 of the GNU General Public
* License as published by the Free Software Foundation .
*/
# include <linux/bpf.h>
# include <linux/if_link.h>
# include <assert.h>
# include <errno.h>
# include <signal.h>
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
2019-06-25 03:55:36 +03:00
# include <net/if.h>
2018-04-18 07:42:23 +03:00
# include <sys/resource.h>
# include <arpa/inet.h>
# include <netinet/ether.h>
# include <unistd.h>
# include <time.h>
2019-06-15 18:14:47 +03:00
# include "bpf.h"
# include "libbpf.h"
2018-04-18 07:42:23 +03:00
# define STATS_INTERVAL_S 2U
static int ifindex = - 1 ;
2019-02-02 00:42:28 +03:00
static __u32 xdp_flags = XDP_FLAGS_UPDATE_IF_NOEXIST ;
2019-02-02 00:42:30 +03:00
static __u32 prog_id ;
2018-04-18 07:42:23 +03:00
static void int_exit ( int sig )
{
2019-02-02 00:42:30 +03:00
__u32 curr_prog_id = 0 ;
if ( ifindex > - 1 ) {
if ( bpf_get_link_xdp_id ( ifindex , & curr_prog_id , xdp_flags ) ) {
printf ( " bpf_get_link_xdp_id failed \n " ) ;
exit ( 1 ) ;
}
if ( prog_id = = curr_prog_id )
bpf_set_link_xdp_fd ( ifindex , - 1 , xdp_flags ) ;
else if ( ! curr_prog_id )
printf ( " couldn't find a prog id on a given iface \n " ) ;
else
printf ( " program on interface changed, not removing \n " ) ;
}
2018-04-18 07:42:23 +03:00
exit ( 0 ) ;
}
/* simple "icmp packet too big sent" counter
*/
2018-05-10 20:24:43 +03:00
static void poll_stats ( unsigned int map_fd , unsigned int kill_after_s )
2018-04-18 07:42:23 +03:00
{
time_t started_at = time ( NULL ) ;
__u64 value = 0 ;
int key = 0 ;
while ( ! kill_after_s | | time ( NULL ) - started_at < = kill_after_s ) {
sleep ( STATS_INTERVAL_S ) ;
2018-05-10 20:24:43 +03:00
assert ( bpf_map_lookup_elem ( map_fd , & key , & value ) = = 0 ) ;
2018-04-18 07:42:23 +03:00
printf ( " icmp \" packet too big \" sent: %10llu pkts \n " , value ) ;
}
}
static void usage ( const char * cmd )
{
printf ( " Start a XDP prog which send ICMP \" packet too big \" \n "
" messages if ingress packet is bigger then MAX_SIZE bytes \n " ) ;
printf ( " Usage: %s [...] \n " , cmd ) ;
2019-06-25 03:55:36 +03:00
printf ( " -i <ifname|ifindex> Interface \n " ) ;
2018-04-18 07:42:23 +03:00
printf ( " -T <stop-after-X-seconds> Default: 0 (forever) \n " ) ;
printf ( " -S use skb-mode \n " ) ;
printf ( " -N enforce native mode \n " ) ;
2019-02-02 00:42:28 +03:00
printf ( " -F force loading prog \n " ) ;
2018-04-18 07:42:23 +03:00
printf ( " -h Display this help \n " ) ;
}
int main ( int argc , char * * argv )
{
2018-05-10 20:24:43 +03:00
struct rlimit r = { RLIM_INFINITY , RLIM_INFINITY } ;
struct bpf_prog_load_attr prog_load_attr = {
. prog_type = BPF_PROG_TYPE_XDP ,
} ;
2018-04-18 07:42:23 +03:00
unsigned char opt_flags [ 256 ] = { } ;
2019-02-02 00:42:28 +03:00
const char * optstr = " i:T:SNFh " ;
2019-02-02 00:42:30 +03:00
struct bpf_prog_info info = { } ;
__u32 info_len = sizeof ( info ) ;
2018-04-18 07:42:23 +03:00
unsigned int kill_after_s = 0 ;
2018-05-10 20:24:43 +03:00
int i , prog_fd , map_fd , opt ;
struct bpf_object * obj ;
struct bpf_map * map ;
2018-04-18 07:42:23 +03:00
char filename [ 256 ] ;
2019-02-02 00:42:30 +03:00
int err ;
2018-04-18 07:42:23 +03:00
for ( i = 0 ; i < strlen ( optstr ) ; i + + )
if ( optstr [ i ] ! = ' h ' & & ' a ' < = optstr [ i ] & & optstr [ i ] < = ' z ' )
opt_flags [ ( unsigned char ) optstr [ i ] ] = 1 ;
while ( ( opt = getopt ( argc , argv , optstr ) ) ! = - 1 ) {
switch ( opt ) {
case ' i ' :
2019-06-25 03:55:36 +03:00
ifindex = if_nametoindex ( optarg ) ;
if ( ! ifindex )
ifindex = atoi ( optarg ) ;
2018-04-18 07:42:23 +03:00
break ;
case ' T ' :
kill_after_s = atoi ( optarg ) ;
break ;
case ' S ' :
xdp_flags | = XDP_FLAGS_SKB_MODE ;
break ;
case ' N ' :
xdp_flags | = XDP_FLAGS_DRV_MODE ;
break ;
2019-02-02 00:42:28 +03:00
case ' F ' :
xdp_flags & = ~ XDP_FLAGS_UPDATE_IF_NOEXIST ;
break ;
2018-04-18 07:42:23 +03:00
default :
usage ( argv [ 0 ] ) ;
return 1 ;
}
opt_flags [ opt ] = 0 ;
}
for ( i = 0 ; i < strlen ( optstr ) ; i + + ) {
if ( opt_flags [ ( unsigned int ) optstr [ i ] ] ) {
fprintf ( stderr , " Missing argument -%c \n " , optstr [ i ] ) ;
usage ( argv [ 0 ] ) ;
return 1 ;
}
}
if ( setrlimit ( RLIMIT_MEMLOCK , & r ) ) {
perror ( " setrlimit(RLIMIT_MEMLOCK, RLIM_INFINITY) " ) ;
return 1 ;
}
2019-06-25 03:55:36 +03:00
if ( ! ifindex ) {
fprintf ( stderr , " Invalid ifname \n " ) ;
return 1 ;
}
2018-04-18 07:42:23 +03:00
snprintf ( filename , sizeof ( filename ) , " %s_kern.o " , argv [ 0 ] ) ;
2018-05-10 20:24:43 +03:00
prog_load_attr . file = filename ;
if ( bpf_prog_load_xattr ( & prog_load_attr , & obj , & prog_fd ) )
return 1 ;
2018-04-18 07:42:23 +03:00
2018-05-10 20:24:43 +03:00
map = bpf_map__next ( NULL , obj ) ;
if ( ! map ) {
printf ( " finding a map in obj file failed \n " ) ;
2018-04-18 07:42:23 +03:00
return 1 ;
}
2018-05-10 20:24:43 +03:00
map_fd = bpf_map__fd ( map ) ;
2018-04-18 07:42:23 +03:00
2018-05-10 20:24:43 +03:00
if ( ! prog_fd ) {
2018-04-18 07:42:23 +03:00
printf ( " load_bpf_file: %s \n " , strerror ( errno ) ) ;
return 1 ;
}
signal ( SIGINT , int_exit ) ;
signal ( SIGTERM , int_exit ) ;
2018-05-10 20:24:43 +03:00
if ( bpf_set_link_xdp_fd ( ifindex , prog_fd , xdp_flags ) < 0 ) {
2018-04-18 07:42:23 +03:00
printf ( " link set xdp fd failed \n " ) ;
return 1 ;
}
2019-02-02 00:42:30 +03:00
err = bpf_obj_get_info_by_fd ( prog_fd , & info , & info_len ) ;
if ( err ) {
printf ( " can't get prog info - %s \n " , strerror ( errno ) ) ;
return 1 ;
}
prog_id = info . id ;
2018-04-18 07:42:23 +03:00
2019-02-02 00:42:30 +03:00
poll_stats ( map_fd , kill_after_s ) ;
int_exit ( 0 ) ;
2018-04-18 07:42:23 +03:00
return 0 ;
}