2017-12-01 15:09:00 -08:00
#!/usr/bin/python3
# Copyright (C) 2017 Netronome Systems, Inc.
#
# This software is licensed under the GNU General License Version 2,
# June 1991 as shown in the file COPYING in the top-level directory of this
# source tree.
#
# THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS"
# WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
# BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE
# OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME
# THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
from datetime import datetime
import argparse
import json
import os
import pprint
2017-12-27 18:39:11 -08:00
import random
import string
2018-01-17 19:13:31 -08:00
import struct
2017-12-01 15:09:00 -08:00
import subprocess
import time
logfile = None
log_level = 1
2018-01-23 11:22:53 -08:00
skip_extack = False
2017-12-01 15:09:00 -08:00
bpf_test_dir = os . path . dirname ( os . path . realpath ( __file__ ) )
pp = pprint . PrettyPrinter ( )
devs = [ ] # devices we created for clean up
files = [ ] # files to be removed
2017-12-27 18:39:11 -08:00
netns = [ ] # net namespaces to be removed
2017-12-01 15:09:00 -08:00
def log_get_sec ( level = 0 ) :
return " * " * ( log_level + level )
def log_level_inc ( add = 1 ) :
global log_level
log_level + = add
def log_level_dec ( sub = 1 ) :
global log_level
log_level - = sub
def log_level_set ( level ) :
global log_level
log_level = level
def log ( header , data , level = None ) :
"""
Output to an optional log .
"""
if logfile is None :
return
if level is not None :
log_level_set ( level )
if not isinstance ( data , str ) :
data = pp . pformat ( data )
if len ( header ) :
logfile . write ( " \n " + log_get_sec ( ) + " " )
logfile . write ( header )
if len ( header ) and len ( data . strip ( ) ) :
logfile . write ( " \n " )
logfile . write ( data )
def skip ( cond , msg ) :
if not cond :
return
print ( " SKIP: " + msg )
log ( " SKIP: " + msg , " " , level = 1 )
os . sys . exit ( 0 )
def fail ( cond , msg ) :
if not cond :
return
print ( " FAIL: " + msg )
log ( " FAIL: " + msg , " " , level = 1 )
os . sys . exit ( 1 )
def start_test ( msg ) :
log ( msg , " " , level = 1 )
log_level_inc ( )
print ( msg )
def cmd ( cmd , shell = True , include_stderr = False , background = False , fail = True ) :
"""
Run a command in subprocess and return tuple of ( retval , stdout ) ;
optionally return stderr as well as third value .
"""
proc = subprocess . Popen ( cmd , shell = shell , stdout = subprocess . PIPE ,
stderr = subprocess . PIPE )
if background :
msg = " %s START: %s " % ( log_get_sec ( 1 ) ,
datetime . now ( ) . strftime ( " % H: % M: % S. %f " ) )
log ( " BKG " + proc . args , msg )
return proc
return cmd_result ( proc , include_stderr = include_stderr , fail = fail )
def cmd_result ( proc , include_stderr = False , fail = False ) :
stdout , stderr = proc . communicate ( )
stdout = stdout . decode ( " utf-8 " )
stderr = stderr . decode ( " utf-8 " )
proc . stdout . close ( )
proc . stderr . close ( )
stderr = " \n " + stderr
if stderr [ - 1 ] == " \n " :
stderr = stderr [ : - 1 ]
sec = log_get_sec ( 1 )
log ( " CMD " + proc . args ,
" RETCODE: %d \n %s STDOUT: \n %s %s STDERR: %s \n %s END: %s " %
( proc . returncode , sec , stdout , sec , stderr ,
sec , datetime . now ( ) . strftime ( " % H: % M: % S. %f " ) ) )
if proc . returncode != 0 and fail :
if len ( stderr ) > 0 and stderr [ - 1 ] == " \n " :
stderr = stderr [ : - 1 ]
raise Exception ( " Command failed: %s \n %s " % ( proc . args , stderr ) )
if include_stderr :
return proc . returncode , stdout , stderr
else :
return proc . returncode , stdout
def rm ( f ) :
cmd ( " rm -f %s " % ( f ) )
if f in files :
files . remove ( f )
2018-01-23 11:22:53 -08:00
def tool ( name , args , flags , JSON = True , ns = " " , fail = True , include_stderr = False ) :
2017-12-01 15:09:00 -08:00
params = " "
if JSON :
params + = " %s " % ( flags [ " json " ] )
2017-12-27 18:39:11 -08:00
if ns != " " :
ns = " ip netns exec %s " % ( ns )
2018-01-23 11:22:53 -08:00
if include_stderr :
ret , stdout , stderr = cmd ( ns + name + " " + params + args ,
fail = fail , include_stderr = True )
else :
ret , stdout = cmd ( ns + name + " " + params + args ,
fail = fail , include_stderr = False )
if JSON and len ( stdout . strip ( ) ) != 0 :
out = json . loads ( stdout )
else :
out = stdout
if include_stderr :
return ret , out , stderr
2017-12-01 15:09:00 -08:00
else :
return ret , out
2017-12-27 18:39:11 -08:00
def bpftool ( args , JSON = True , ns = " " , fail = True ) :
return tool ( " bpftool " , args , { " json " : " -p " } , JSON = JSON , ns = ns , fail = fail )
2017-12-01 15:09:00 -08:00
2017-12-27 18:39:11 -08:00
def bpftool_prog_list ( expected = None , ns = " " ) :
_ , progs = bpftool ( " prog show " , JSON = True , ns = ns , fail = True )
2017-12-01 15:09:00 -08:00
if expected is not None :
if len ( progs ) != expected :
fail ( True , " %d BPF programs loaded, expected %d " %
( len ( progs ) , expected ) )
return progs
2018-01-17 19:13:31 -08:00
def bpftool_map_list ( expected = None , ns = " " ) :
_ , maps = bpftool ( " map show " , JSON = True , ns = ns , fail = True )
if expected is not None :
if len ( maps ) != expected :
fail ( True , " %d BPF maps loaded, expected %d " %
( len ( maps ) , expected ) )
return maps
2017-12-01 15:09:00 -08:00
def bpftool_prog_list_wait ( expected = 0 , n_retry = 20 ) :
for i in range ( n_retry ) :
nprogs = len ( bpftool_prog_list ( ) )
if nprogs == expected :
return
time . sleep ( 0.05 )
raise Exception ( " Time out waiting for program counts to stabilize want %d , have %d " % ( expected , nprogs ) )
2018-01-17 19:13:31 -08:00
def bpftool_map_list_wait ( expected = 0 , n_retry = 20 ) :
for i in range ( n_retry ) :
nmaps = len ( bpftool_map_list ( ) )
if nmaps == expected :
return
time . sleep ( 0.05 )
raise Exception ( " Time out waiting for map counts to stabilize want %d , have %d " % ( expected , nmaps ) )
2018-01-23 11:22:53 -08:00
def ip ( args , force = False , JSON = True , ns = " " , fail = True , include_stderr = False ) :
2017-12-01 15:09:00 -08:00
if force :
args = " -force " + args
2018-01-23 11:22:53 -08:00
return tool ( " ip " , args , { " json " : " -j " } , JSON = JSON , ns = ns ,
fail = fail , include_stderr = include_stderr )
2017-12-01 15:09:00 -08:00
2018-01-23 11:22:53 -08:00
def tc ( args , JSON = True , ns = " " , fail = True , include_stderr = False ) :
return tool ( " tc " , args , { " json " : " -p " } , JSON = JSON , ns = ns ,
fail = fail , include_stderr = include_stderr )
2017-12-01 15:09:00 -08:00
def ethtool ( dev , opt , args , fail = True ) :
return cmd ( " ethtool %s %s %s " % ( opt , dev [ " ifname " ] , args ) , fail = fail )
def bpf_obj ( name , sec = " .text " , path = bpf_test_dir , ) :
return " obj %s sec %s " % ( os . path . join ( path , name ) , sec )
def bpf_pinned ( name ) :
return " pinned %s " % ( name )
def bpf_bytecode ( bytecode ) :
return " bytecode \" %s \" " % ( bytecode )
2017-12-27 18:39:11 -08:00
def mknetns ( n_retry = 10 ) :
for i in range ( n_retry ) :
name = ' ' . join ( [ random . choice ( string . ascii_letters ) for i in range ( 8 ) ] )
ret , _ = ip ( " netns add %s " % ( name ) , fail = False )
if ret == 0 :
netns . append ( name )
return name
return None
2018-01-17 19:13:31 -08:00
def int2str ( fmt , val ) :
ret = [ ]
for b in struct . pack ( fmt , val ) :
ret . append ( int ( b ) )
return " " . join ( map ( lambda x : str ( x ) , ret ) )
def str2int ( strtab ) :
inttab = [ ]
for i in strtab :
inttab . append ( int ( i , 16 ) )
ba = bytearray ( inttab )
if len ( strtab ) == 4 :
fmt = " I "
elif len ( strtab ) == 8 :
fmt = " Q "
else :
raise Exception ( " String array of len %d can ' t be unpacked to an int " %
( len ( strtab ) ) )
return struct . unpack ( fmt , ba ) [ 0 ]
2017-12-01 15:09:00 -08:00
class DebugfsDir :
"""
Class for accessing DebugFS directories as a dictionary .
"""
def __init__ ( self , path ) :
self . path = path
self . _dict = self . _debugfs_dir_read ( path )
def __len__ ( self ) :
return len ( self . _dict . keys ( ) )
def __getitem__ ( self , key ) :
if type ( key ) is int :
key = list ( self . _dict . keys ( ) ) [ key ]
return self . _dict [ key ]
def __setitem__ ( self , key , value ) :
log ( " DebugFS set %s = %s " % ( key , value ) , " " )
log_level_inc ( )
cmd ( " echo ' %s ' > %s / %s " % ( value , self . path , key ) )
log_level_dec ( )
_ , out = cmd ( ' cat %s / %s ' % ( self . path , key ) )
self . _dict [ key ] = out . strip ( )
def _debugfs_dir_read ( self , path ) :
dfs = { }
log ( " DebugFS state for %s " % ( path ) , " " )
log_level_inc ( add = 2 )
_ , out = cmd ( ' ls ' + path )
for f in out . split ( ) :
p = os . path . join ( path , f )
if os . path . isfile ( p ) :
_ , out = cmd ( ' cat %s / %s ' % ( path , f ) )
dfs [ f ] = out . strip ( )
elif os . path . isdir ( p ) :
dfs [ f ] = DebugfsDir ( p )
else :
raise Exception ( " %s is neither file nor directory " % ( p ) )
log_level_dec ( )
log ( " DebugFS state " , dfs )
log_level_dec ( )
return dfs
class NetdevSim :
"""
Class for netdevsim netdevice and its attributes .
"""
def __init__ ( self ) :
self . dev = self . _netdevsim_create ( )
devs . append ( self )
2017-12-27 18:39:11 -08:00
self . ns = " "
2017-12-01 15:09:00 -08:00
self . dfs_dir = ' /sys/kernel/debug/netdevsim/ %s ' % ( self . dev [ ' ifname ' ] )
self . dfs_refresh ( )
def __getitem__ ( self , key ) :
return self . dev [ key ]
def _netdevsim_create ( self ) :
_ , old = ip ( " link show " )
ip ( " link add sim %d type netdevsim " )
_ , new = ip ( " link show " )
for dev in new :
f = filter ( lambda x : x [ " ifname " ] == dev [ " ifname " ] , old )
if len ( list ( f ) ) == 0 :
return dev
raise Exception ( " failed to create netdevsim device " )
def remove ( self ) :
devs . remove ( self )
2017-12-27 18:39:11 -08:00
ip ( " link del dev %s " % ( self . dev [ " ifname " ] ) , ns = self . ns )
2017-12-01 15:09:00 -08:00
def dfs_refresh ( self ) :
self . dfs = DebugfsDir ( self . dfs_dir )
return self . dfs
def dfs_num_bound_progs ( self ) :
path = os . path . join ( self . dfs_dir , " bpf_bound_progs " )
_ , progs = cmd ( ' ls %s ' % ( path ) )
return len ( progs . split ( ) )
def dfs_get_bound_progs ( self , expected ) :
progs = DebugfsDir ( os . path . join ( self . dfs_dir , " bpf_bound_progs " ) )
if expected is not None :
if len ( progs ) != expected :
fail ( True , " %d BPF programs bound, expected %d " %
( len ( progs ) , expected ) )
return progs
def wait_for_flush ( self , bound = 0 , total = 0 , n_retry = 20 ) :
for i in range ( n_retry ) :
nbound = self . dfs_num_bound_progs ( )
nprogs = len ( bpftool_prog_list ( ) )
if nbound == bound and nprogs == total :
return
time . sleep ( 0.05 )
raise Exception ( " Time out waiting for program counts to stabilize want %d / %d , have %d bound, %d loaded " % ( bound , total , nbound , nprogs ) )
2017-12-27 18:39:11 -08:00
def set_ns ( self , ns ) :
name = " 1 " if ns == " " else ns
ip ( " link set dev %s netns %s " % ( self . dev [ " ifname " ] , name ) , ns = self . ns )
self . ns = ns
2017-12-01 15:09:00 -08:00
def set_mtu ( self , mtu , fail = True ) :
return ip ( " link set dev %s mtu %d " % ( self . dev [ " ifname " ] , mtu ) ,
fail = fail )
2018-01-23 11:22:55 -08:00
def set_xdp ( self , bpf , mode , force = False , JSON = True , verbose = False ,
2018-01-23 11:22:53 -08:00
fail = True , include_stderr = False ) :
2018-01-23 11:22:55 -08:00
if verbose :
bpf + = " verbose "
2017-12-01 15:09:00 -08:00
return ip ( " link set dev %s xdp %s %s " % ( self . dev [ " ifname " ] , mode , bpf ) ,
2018-01-23 11:22:53 -08:00
force = force , JSON = JSON ,
fail = fail , include_stderr = include_stderr )
2017-12-01 15:09:00 -08:00
2018-01-23 11:22:53 -08:00
def unset_xdp ( self , mode , force = False , JSON = True ,
fail = True , include_stderr = False ) :
2017-12-01 15:09:00 -08:00
return ip ( " link set dev %s xdp %s off " % ( self . dev [ " ifname " ] , mode ) ,
2018-01-23 11:22:53 -08:00
force = force , JSON = JSON ,
fail = fail , include_stderr = include_stderr )
2017-12-01 15:09:00 -08:00
def ip_link_show ( self , xdp ) :
_ , link = ip ( " link show dev %s " % ( self [ ' ifname ' ] ) )
if len ( link ) > 1 :
raise Exception ( " Multiple objects on ip link show " )
if len ( link ) < 1 :
return { }
fail ( xdp != " xdp " in link ,
" XDP program not reporting in iplink (reported %s , expected %s ) " %
( " xdp " in link , xdp ) )
return link [ 0 ]
def tc_add_ingress ( self ) :
tc ( " qdisc add dev %s ingress " % ( self [ ' ifname ' ] ) )
def tc_del_ingress ( self ) :
tc ( " qdisc del dev %s ingress " % ( self [ ' ifname ' ] ) )
def tc_flush_filters ( self , bound = 0 , total = 0 ) :
self . tc_del_ingress ( )
self . tc_add_ingress ( )
self . wait_for_flush ( bound = bound , total = total )
def tc_show_ingress ( self , expected = None ) :
# No JSON support, oh well...
flags = [ " skip_sw " , " skip_hw " , " in_hw " ]
named = [ " protocol " , " pref " , " chain " , " handle " , " id " , " tag " ]
args = " -s filter show dev %s ingress " % ( self [ ' ifname ' ] )
_ , out = tc ( args , JSON = False )
filters = [ ]
lines = out . split ( ' \n ' )
for line in lines :
words = line . split ( )
if " handle " not in words :
continue
fltr = { }
for flag in flags :
fltr [ flag ] = flag in words
for name in named :
try :
idx = words . index ( name )
fltr [ name ] = words [ idx + 1 ]
except ValueError :
pass
filters . append ( fltr )
if expected is not None :
fail ( len ( filters ) != expected ,
" %d ingress filters loaded, expected %d " %
( len ( filters ) , expected ) )
return filters
2018-01-23 11:22:56 -08:00
def cls_filter_op ( self , op , qdisc = " ingress " , prio = None , handle = None ,
cls = " " , params = " " ,
fail = True , include_stderr = False ) :
spec = " "
if prio is not None :
spec + = " prio %d " % ( prio )
if handle :
spec + = " handle %s " % ( handle )
return tc ( " filter {op} dev {dev} {qdisc} {spec} {cls} {params} " \
. format ( op = op , dev = self [ ' ifname ' ] , qdisc = qdisc , spec = spec ,
cls = cls , params = params ) ,
fail = fail , include_stderr = include_stderr )
def cls_bpf_add_filter ( self , bpf , op = " add " , prio = None , handle = None ,
da = False , verbose = False ,
skip_sw = False , skip_hw = False ,
fail = True , include_stderr = False ) :
cls = " bpf " + bpf
2017-12-01 15:09:00 -08:00
params = " "
if da :
params + = " da "
2018-01-23 11:22:55 -08:00
if verbose :
params + = " verbose "
2017-12-01 15:09:00 -08:00
if skip_sw :
params + = " skip_sw "
if skip_hw :
params + = " skip_hw "
2018-01-23 11:22:56 -08:00
return self . cls_filter_op ( op = op , prio = prio , handle = handle , cls = cls ,
params = params ,
fail = fail , include_stderr = include_stderr )
2017-12-01 15:09:00 -08:00
def set_ethtool_tc_offloads ( self , enable , fail = True ) :
args = " hw-tc-offload %s " % ( " on " if enable else " off " )
return ethtool ( self , " -K " , args , fail = fail )
################################################################################
def clean_up ( ) :
2018-01-17 19:13:31 -08:00
global files , netns , devs
2017-12-01 15:09:00 -08:00
for dev in devs :
dev . remove ( )
for f in files :
cmd ( " rm -f %s " % ( f ) )
2017-12-27 18:39:11 -08:00
for ns in netns :
cmd ( " ip netns delete %s " % ( ns ) )
2018-01-17 19:13:31 -08:00
files = [ ]
netns = [ ]
2017-12-01 15:09:00 -08:00
def pin_prog ( file_name , idx = 0 ) :
progs = bpftool_prog_list ( expected = ( idx + 1 ) )
prog = progs [ idx ]
bpftool ( " prog pin id %d %s " % ( prog [ " id " ] , file_name ) )
files . append ( file_name )
return file_name , bpf_pinned ( file_name )
2018-01-17 19:13:31 -08:00
def pin_map ( file_name , idx = 0 , expected = 1 ) :
maps = bpftool_map_list ( expected = expected )
m = maps [ idx ]
bpftool ( " map pin id %d %s " % ( m [ " id " ] , file_name ) )
files . append ( file_name )
return file_name , bpf_pinned ( file_name )
def check_dev_info_removed ( prog_file = None , map_file = None ) :
bpftool_prog_list ( expected = 0 )
ret , err = bpftool ( " prog show pin %s " % ( prog_file ) , fail = False )
fail ( ret == 0 , " Showing prog with removed device did not fail " )
fail ( err [ " error " ] . find ( " No such device " ) == - 1 ,
" Showing prog with removed device expected ENODEV, error is %s " %
( err [ " error " ] ) )
bpftool_map_list ( expected = 0 )
ret , err = bpftool ( " map show pin %s " % ( map_file ) , fail = False )
fail ( ret == 0 , " Showing map with removed device did not fail " )
fail ( err [ " error " ] . find ( " No such device " ) == - 1 ,
" Showing map with removed device expected ENODEV, error is %s " %
( err [ " error " ] ) )
def check_dev_info ( other_ns , ns , prog_file = None , map_file = None , removed = False ) :
progs = bpftool_prog_list ( expected = 1 , ns = ns )
2017-12-27 18:39:11 -08:00
prog = progs [ 0 ]
fail ( " dev " not in prog . keys ( ) , " Device parameters not reported " )
dev = prog [ " dev " ]
fail ( " ifindex " not in dev . keys ( ) , " Device parameters not reported " )
fail ( " ns_dev " not in dev . keys ( ) , " Device parameters not reported " )
fail ( " ns_inode " not in dev . keys ( ) , " Device parameters not reported " )
2018-01-17 19:13:31 -08:00
if not other_ns :
2017-12-27 18:39:11 -08:00
fail ( " ifname " not in dev . keys ( ) , " Ifname not reported " )
fail ( dev [ " ifname " ] != sim [ " ifname " ] ,
" Ifname incorrect %s vs %s " % ( dev [ " ifname " ] , sim [ " ifname " ] ) )
else :
fail ( " ifname " in dev . keys ( ) , " Ifname is reported for other ns " )
2018-01-17 19:13:31 -08:00
maps = bpftool_map_list ( expected = 2 , ns = ns )
for m in maps :
fail ( " dev " not in m . keys ( ) , " Device parameters not reported " )
fail ( dev != m [ " dev " ] , " Map ' s device different than program ' s " )
2017-12-27 18:39:11 -08:00
2018-01-23 11:22:53 -08:00
def check_extack ( output , reference , args ) :
if skip_extack :
return
lines = output . split ( " \n " )
comp = len ( lines ) > = 2 and lines [ 1 ] == reference
fail ( not comp , " Missing or incorrect netlink extack message " )
def check_extack_nsim ( output , reference , args ) :
check_extack ( output , " Error: netdevsim: " + reference , args )
2018-01-23 11:22:55 -08:00
def check_verifier_log ( output , reference ) :
lines = output . split ( " \n " )
for l in reversed ( lines ) :
if l == reference :
return
fail ( True , " Missing or incorrect message from netdevsim in verifier log " )
2017-12-01 15:09:00 -08:00
# Parse command line
parser = argparse . ArgumentParser ( )
parser . add_argument ( " --log " , help = " output verbose log to given file " )
args = parser . parse_args ( )
if args . log :
logfile = open ( args . log , ' w+ ' )
logfile . write ( " # -*-Org-*- " )
log ( " Prepare... " , " " , level = 1 )
log_level_inc ( )
# Check permissions
skip ( os . getuid ( ) != 0 , " test must be run as root " )
# Check tools
ret , progs = bpftool ( " prog " , fail = False )
skip ( ret != 0 , " bpftool not installed " )
# Check no BPF programs are loaded
skip ( len ( progs ) != 0 , " BPF programs already loaded on the system " )
# Check netdevsim
ret , out = cmd ( " modprobe netdevsim " , fail = False )
skip ( ret != 0 , " netdevsim module could not be loaded " )
# Check debugfs
_ , out = cmd ( " mount " )
if out . find ( " /sys/kernel/debug type debugfs " ) == - 1 :
cmd ( " mount -t debugfs none /sys/kernel/debug " )
# Check samples are compiled
2018-01-17 19:13:31 -08:00
samples = [ " sample_ret0.o " , " sample_map_ret0.o " ]
2017-12-01 15:09:00 -08:00
for s in samples :
ret , out = cmd ( " ls %s / %s " % ( bpf_test_dir , s ) , fail = False )
skip ( ret != 0 , " sample %s / %s not found, please compile it " %
( bpf_test_dir , s ) )
2018-01-23 11:22:53 -08:00
# Check if iproute2 is built with libmnl (needed by extack support)
_ , _ , err = cmd ( " tc qdisc delete dev lo handle 0 " ,
fail = False , include_stderr = True )
if err . find ( " Error: Failed to find qdisc with specified handle. " ) == - 1 :
print ( " Warning: no extack message in iproute2 output, libmnl missing? " )
log ( " Warning: no extack message in iproute2 output, libmnl missing? " , " " )
skip_extack = True
2017-12-27 18:39:11 -08:00
# Check if net namespaces seem to work
ns = mknetns ( )
skip ( ns is None , " Could not create a net namespace " )
cmd ( " ip netns delete %s " % ( ns ) )
netns = [ ]
2017-12-01 15:09:00 -08:00
try :
obj = bpf_obj ( " sample_ret0.o " )
bytecode = bpf_bytecode ( " 1,6 0 0 4294967295, " )
start_test ( " Test destruction of generic XDP... " )
sim = NetdevSim ( )
sim . set_xdp ( obj , " generic " )
sim . remove ( )
bpftool_prog_list_wait ( expected = 0 )
sim = NetdevSim ( )
sim . tc_add_ingress ( )
start_test ( " Test TC non-offloaded... " )
ret , _ = sim . cls_bpf_add_filter ( obj , skip_hw = True , fail = False )
fail ( ret != 0 , " Software TC filter did not load " )
start_test ( " Test TC non-offloaded isn ' t getting bound... " )
ret , _ = sim . cls_bpf_add_filter ( obj , fail = False )
fail ( ret != 0 , " Software TC filter did not load " )
sim . dfs_get_bound_progs ( expected = 0 )
sim . tc_flush_filters ( )
start_test ( " Test TC offloads are off by default... " )
2018-01-23 11:22:53 -08:00
ret , _ , err = sim . cls_bpf_add_filter ( obj , skip_sw = True ,
fail = False , include_stderr = True )
2017-12-01 15:09:00 -08:00
fail ( ret == 0 , " TC filter loaded without enabling TC offloads " )
2018-01-23 11:22:53 -08:00
check_extack ( err , " Error: TC offload is disabled on net device. " , args )
2017-12-01 15:09:00 -08:00
sim . wait_for_flush ( )
sim . set_ethtool_tc_offloads ( True )
sim . dfs [ " bpf_tc_non_bound_accept " ] = " Y "
start_test ( " Test TC offload by default... " )
ret , _ = sim . cls_bpf_add_filter ( obj , fail = False )
fail ( ret != 0 , " Software TC filter did not load " )
sim . dfs_get_bound_progs ( expected = 0 )
ingress = sim . tc_show_ingress ( expected = 1 )
fltr = ingress [ 0 ]
fail ( not fltr [ " in_hw " ] , " Filter not offloaded by default " )
sim . tc_flush_filters ( )
start_test ( " Test TC cBPF bytcode tries offload by default... " )
ret , _ = sim . cls_bpf_add_filter ( bytecode , fail = False )
fail ( ret != 0 , " Software TC filter did not load " )
sim . dfs_get_bound_progs ( expected = 0 )
ingress = sim . tc_show_ingress ( expected = 1 )
fltr = ingress [ 0 ]
fail ( not fltr [ " in_hw " ] , " Bytecode not offloaded by default " )
sim . tc_flush_filters ( )
sim . dfs [ " bpf_tc_non_bound_accept " ] = " N "
start_test ( " Test TC cBPF unbound bytecode doesn ' t offload... " )
2018-01-23 11:22:53 -08:00
ret , _ , err = sim . cls_bpf_add_filter ( bytecode , skip_sw = True ,
fail = False , include_stderr = True )
2017-12-01 15:09:00 -08:00
fail ( ret == 0 , " TC bytecode loaded for offload " )
2018-01-23 11:22:53 -08:00
check_extack_nsim ( err , " netdevsim configured to reject unbound programs. " ,
args )
2017-12-01 15:09:00 -08:00
sim . wait_for_flush ( )
2018-01-23 11:22:56 -08:00
start_test ( " Test TC replace... " )
sim . cls_bpf_add_filter ( obj , prio = 1 , handle = 1 )
sim . cls_bpf_add_filter ( obj , op = " replace " , prio = 1 , handle = 1 )
sim . cls_filter_op ( op = " delete " , prio = 1 , handle = 1 , cls = " bpf " )
sim . cls_bpf_add_filter ( obj , prio = 1 , handle = 1 , skip_sw = True )
sim . cls_bpf_add_filter ( obj , op = " replace " , prio = 1 , handle = 1 , skip_sw = True )
sim . cls_filter_op ( op = " delete " , prio = 1 , handle = 1 , cls = " bpf " )
sim . cls_bpf_add_filter ( obj , prio = 1 , handle = 1 , skip_hw = True )
sim . cls_bpf_add_filter ( obj , op = " replace " , prio = 1 , handle = 1 , skip_hw = True )
sim . cls_filter_op ( op = " delete " , prio = 1 , handle = 1 , cls = " bpf " )
start_test ( " Test TC replace bad flags... " )
for i in range ( 3 ) :
for j in range ( 3 ) :
ret , _ = sim . cls_bpf_add_filter ( obj , op = " replace " , prio = 1 , handle = 1 ,
skip_sw = ( j == 1 ) , skip_hw = ( j == 2 ) ,
fail = False )
fail ( bool ( ret ) != bool ( j ) ,
" Software TC incorrect load in replace test, iteration %d " %
( j ) )
sim . cls_filter_op ( op = " delete " , prio = 1 , handle = 1 , cls = " bpf " )
sim . tc_flush_filters ( )
2017-12-01 15:09:00 -08:00
start_test ( " Test TC offloads work... " )
2018-01-23 11:22:55 -08:00
ret , _ , err = sim . cls_bpf_add_filter ( obj , verbose = True , skip_sw = True ,
fail = False , include_stderr = True )
2017-12-01 15:09:00 -08:00
fail ( ret != 0 , " TC filter did not load with TC offloads enabled " )
2018-01-23 11:22:55 -08:00
check_verifier_log ( err , " [netdevsim] Hello from netdevsim! " )
2017-12-01 15:09:00 -08:00
start_test ( " Test TC offload basics... " )
dfs = sim . dfs_get_bound_progs ( expected = 1 )
progs = bpftool_prog_list ( expected = 1 )
ingress = sim . tc_show_ingress ( expected = 1 )
dprog = dfs [ 0 ]
prog = progs [ 0 ]
fltr = ingress [ 0 ]
fail ( fltr [ " skip_hw " ] , " TC does reports ' skip_hw ' on offloaded filter " )
fail ( not fltr [ " in_hw " ] , " TC does not report ' in_hw ' for offloaded filter " )
fail ( not fltr [ " skip_sw " ] , " TC does not report ' skip_sw ' back " )
start_test ( " Test TC offload is device-bound... " )
fail ( str ( prog [ " id " ] ) != fltr [ " id " ] , " Program IDs don ' t match " )
fail ( prog [ " tag " ] != fltr [ " tag " ] , " Program tags don ' t match " )
fail ( fltr [ " id " ] != dprog [ " id " ] , " Program IDs don ' t match " )
fail ( dprog [ " state " ] != " xlated " , " Offloaded program state not translated " )
fail ( dprog [ " loaded " ] != " Y " , " Offloaded program is not loaded " )
start_test ( " Test disabling TC offloads is rejected while filters installed... " )
ret , _ = sim . set_ethtool_tc_offloads ( False , fail = False )
fail ( ret == 0 , " Driver should refuse to disable TC offloads with filters installed... " )
start_test ( " Test qdisc removal frees things... " )
sim . tc_flush_filters ( )
sim . tc_show_ingress ( expected = 0 )
start_test ( " Test disabling TC offloads is OK without filters... " )
ret , _ = sim . set_ethtool_tc_offloads ( False , fail = False )
fail ( ret != 0 ,
" Driver refused to disable TC offloads without filters installed... " )
sim . set_ethtool_tc_offloads ( True )
start_test ( " Test destroying device gets rid of TC filters... " )
sim . cls_bpf_add_filter ( obj , skip_sw = True )
sim . remove ( )
bpftool_prog_list_wait ( expected = 0 )
sim = NetdevSim ( )
sim . set_ethtool_tc_offloads ( True )
start_test ( " Test destroying device gets rid of XDP... " )
sim . set_xdp ( obj , " offload " )
sim . remove ( )
bpftool_prog_list_wait ( expected = 0 )
sim = NetdevSim ( )
sim . set_ethtool_tc_offloads ( True )
start_test ( " Test XDP prog reporting... " )
sim . set_xdp ( obj , " drv " )
ipl = sim . ip_link_show ( xdp = True )
progs = bpftool_prog_list ( expected = 1 )
fail ( ipl [ " xdp " ] [ " prog " ] [ " id " ] != progs [ 0 ] [ " id " ] ,
" Loaded program has wrong ID " )
start_test ( " Test XDP prog replace without force... " )
ret , _ = sim . set_xdp ( obj , " drv " , fail = False )
fail ( ret == 0 , " Replaced XDP program without -force " )
sim . wait_for_flush ( total = 1 )
start_test ( " Test XDP prog replace with force... " )
ret , _ = sim . set_xdp ( obj , " drv " , force = True , fail = False )
fail ( ret != 0 , " Could not replace XDP program with -force " )
bpftool_prog_list_wait ( expected = 1 )
ipl = sim . ip_link_show ( xdp = True )
progs = bpftool_prog_list ( expected = 1 )
fail ( ipl [ " xdp " ] [ " prog " ] [ " id " ] != progs [ 0 ] [ " id " ] ,
" Loaded program has wrong ID " )
2017-12-27 18:39:11 -08:00
fail ( " dev " in progs [ 0 ] . keys ( ) ,
" Device parameters reported for non-offloaded program " )
2017-12-01 15:09:00 -08:00
start_test ( " Test XDP prog replace with bad flags... " )
2018-01-23 11:22:53 -08:00
ret , _ , err = sim . set_xdp ( obj , " offload " , force = True ,
fail = False , include_stderr = True )
2017-12-01 15:09:00 -08:00
fail ( ret == 0 , " Replaced XDP program with a program in different mode " )
2018-01-23 11:22:53 -08:00
check_extack_nsim ( err , " program loaded with different flags. " , args )
ret , _ , err = sim . set_xdp ( obj , " " , force = True ,
fail = False , include_stderr = True )
2017-12-01 15:09:00 -08:00
fail ( ret == 0 , " Replaced XDP program with a program in different mode " )
2018-01-23 11:22:53 -08:00
check_extack_nsim ( err , " program loaded with different flags. " , args )
2017-12-01 15:09:00 -08:00
start_test ( " Test XDP prog remove with bad flags... " )
2018-01-23 11:22:53 -08:00
ret , _ , err = sim . unset_xdp ( " offload " , force = True ,
fail = False , include_stderr = True )
2017-12-01 15:09:00 -08:00
fail ( ret == 0 , " Removed program with a bad mode mode " )
2018-01-23 11:22:53 -08:00
check_extack_nsim ( err , " program loaded with different flags. " , args )
ret , _ , err = sim . unset_xdp ( " " , force = True ,
fail = False , include_stderr = True )
2017-12-01 15:09:00 -08:00
fail ( ret == 0 , " Removed program with a bad mode mode " )
2018-01-23 11:22:53 -08:00
check_extack_nsim ( err , " program loaded with different flags. " , args )
2017-12-01 15:09:00 -08:00
start_test ( " Test MTU restrictions... " )
ret , _ = sim . set_mtu ( 9000 , fail = False )
fail ( ret == 0 ,
" Driver should refuse to increase MTU to 9000 with XDP loaded... " )
sim . unset_xdp ( " drv " )
bpftool_prog_list_wait ( expected = 0 )
sim . set_mtu ( 9000 )
2018-01-23 11:22:53 -08:00
ret , _ , err = sim . set_xdp ( obj , " drv " , fail = False , include_stderr = True )
2017-12-01 15:09:00 -08:00
fail ( ret == 0 , " Driver should refuse to load program with MTU of 9000... " )
2018-01-23 11:22:53 -08:00
check_extack_nsim ( err , " MTU too large w/ XDP enabled. " , args )
2017-12-01 15:09:00 -08:00
sim . set_mtu ( 1500 )
sim . wait_for_flush ( )
start_test ( " Test XDP offload... " )
2018-01-23 11:22:55 -08:00
_ , _ , err = sim . set_xdp ( obj , " offload " , verbose = True , include_stderr = True )
2017-12-01 15:09:00 -08:00
ipl = sim . ip_link_show ( xdp = True )
link_xdp = ipl [ " xdp " ] [ " prog " ]
progs = bpftool_prog_list ( expected = 1 )
prog = progs [ 0 ]
fail ( link_xdp [ " id " ] != prog [ " id " ] , " Loaded program has wrong ID " )
2018-01-23 11:22:55 -08:00
check_verifier_log ( err , " [netdevsim] Hello from netdevsim! " )
2017-12-01 15:09:00 -08:00
start_test ( " Test XDP offload is device bound... " )
dfs = sim . dfs_get_bound_progs ( expected = 1 )
dprog = dfs [ 0 ]
fail ( prog [ " id " ] != link_xdp [ " id " ] , " Program IDs don ' t match " )
fail ( prog [ " tag " ] != link_xdp [ " tag " ] , " Program tags don ' t match " )
fail ( str ( link_xdp [ " id " ] ) != dprog [ " id " ] , " Program IDs don ' t match " )
fail ( dprog [ " state " ] != " xlated " , " Offloaded program state not translated " )
fail ( dprog [ " loaded " ] != " Y " , " Offloaded program is not loaded " )
start_test ( " Test removing XDP program many times... " )
sim . unset_xdp ( " offload " )
sim . unset_xdp ( " offload " )
sim . unset_xdp ( " drv " )
sim . unset_xdp ( " drv " )
sim . unset_xdp ( " " )
sim . unset_xdp ( " " )
bpftool_prog_list_wait ( expected = 0 )
start_test ( " Test attempt to use a program for a wrong device... " )
sim2 = NetdevSim ( )
sim2 . set_xdp ( obj , " offload " )
pin_file , pinned = pin_prog ( " /sys/fs/bpf/tmp " )
2018-01-23 11:22:53 -08:00
ret , _ , err = sim . set_xdp ( pinned , " offload " ,
fail = False , include_stderr = True )
2017-12-01 15:09:00 -08:00
fail ( ret == 0 , " Pinned program loaded for a different device accepted " )
2018-01-23 11:22:53 -08:00
check_extack_nsim ( err , " program bound to different dev. " , args )
2017-12-01 15:09:00 -08:00
sim2 . remove ( )
2018-01-23 11:22:53 -08:00
ret , _ , err = sim . set_xdp ( pinned , " offload " ,
fail = False , include_stderr = True )
2017-12-01 15:09:00 -08:00
fail ( ret == 0 , " Pinned program loaded for a removed device accepted " )
2018-01-23 11:22:53 -08:00
check_extack_nsim ( err , " xdpoffload of non-bound program. " , args )
2017-12-01 15:09:00 -08:00
rm ( pin_file )
bpftool_prog_list_wait ( expected = 0 )
start_test ( " Test mixing of TC and XDP... " )
sim . tc_add_ingress ( )
sim . set_xdp ( obj , " offload " )
2018-01-23 11:22:53 -08:00
ret , _ , err = sim . cls_bpf_add_filter ( obj , skip_sw = True ,
fail = False , include_stderr = True )
2017-12-01 15:09:00 -08:00
fail ( ret == 0 , " Loading TC when XDP active should fail " )
2018-01-23 11:22:53 -08:00
check_extack_nsim ( err , " driver and netdev offload states mismatch. " , args )
2017-12-01 15:09:00 -08:00
sim . unset_xdp ( " offload " )
sim . wait_for_flush ( )
sim . cls_bpf_add_filter ( obj , skip_sw = True )
2018-01-23 11:22:53 -08:00
ret , _ , err = sim . set_xdp ( obj , " offload " , fail = False , include_stderr = True )
2017-12-01 15:09:00 -08:00
fail ( ret == 0 , " Loading XDP when TC active should fail " )
2018-01-23 11:22:53 -08:00
check_extack_nsim ( err , " TC program is already loaded. " , args )
2017-12-01 15:09:00 -08:00
start_test ( " Test binding TC from pinned... " )
pin_file , pinned = pin_prog ( " /sys/fs/bpf/tmp " )
sim . tc_flush_filters ( bound = 1 , total = 1 )
sim . cls_bpf_add_filter ( pinned , da = True , skip_sw = True )
sim . tc_flush_filters ( bound = 1 , total = 1 )
start_test ( " Test binding XDP from pinned... " )
sim . set_xdp ( obj , " offload " )
pin_file , pinned = pin_prog ( " /sys/fs/bpf/tmp2 " , idx = 1 )
sim . set_xdp ( pinned , " offload " , force = True )
sim . unset_xdp ( " offload " )
sim . set_xdp ( pinned , " offload " , force = True )
sim . unset_xdp ( " offload " )
start_test ( " Test offload of wrong type fails... " )
ret , _ = sim . cls_bpf_add_filter ( pinned , da = True , skip_sw = True , fail = False )
fail ( ret == 0 , " Managed to attach XDP program to TC " )
start_test ( " Test asking for TC offload of two filters... " )
sim . cls_bpf_add_filter ( obj , da = True , skip_sw = True )
2018-01-23 11:22:53 -08:00
ret , _ , err = sim . cls_bpf_add_filter ( obj , da = True , skip_sw = True ,
fail = False , include_stderr = True )
2017-12-22 11:16:31 -05:00
fail ( ret == 0 , " Managed to offload two TC filters at the same time " )
2018-01-23 11:22:53 -08:00
check_extack_nsim ( err , " driver and netdev offload states mismatch. " , args )
2017-12-01 15:09:00 -08:00
sim . tc_flush_filters ( bound = 2 , total = 2 )
start_test ( " Test if netdev removal waits for translation... " )
delay_msec = 500
sim . dfs [ " bpf_bind_verifier_delay " ] = delay_msec
start = time . time ( )
cmd_line = " tc filter add dev %s ingress bpf %s da skip_sw " % \
( sim [ ' ifname ' ] , obj )
tc_proc = cmd ( cmd_line , background = True , fail = False )
# Wait for the verifier to start
while sim . dfs_num_bound_progs ( ) < = 2 :
pass
sim . remove ( )
end = time . time ( )
ret , _ = cmd_result ( tc_proc , fail = False )
time_diff = end - start
log ( " Time " , " start: \t %s \n end: \t %s \n diff: \t %s " % ( start , end , time_diff ) )
fail ( ret == 0 , " Managed to load TC filter on a unregistering device " )
delay_sec = delay_msec * 0.001
fail ( time_diff < delay_sec , " Removal process took %s , expected %s " %
( time_diff , delay_sec ) )
2017-12-27 18:39:11 -08:00
# Remove all pinned files and reinstantiate the netdev
clean_up ( )
bpftool_prog_list_wait ( expected = 0 )
sim = NetdevSim ( )
2018-01-17 19:13:31 -08:00
map_obj = bpf_obj ( " sample_map_ret0.o " )
start_test ( " Test loading program with maps... " )
sim . set_xdp ( map_obj , " offload " , JSON = False ) # map fixup msg breaks JSON
2017-12-27 18:39:11 -08:00
start_test ( " Test bpftool bound info reporting (own ns)... " )
check_dev_info ( False , " " )
start_test ( " Test bpftool bound info reporting (other ns)... " )
ns = mknetns ( )
sim . set_ns ( ns )
check_dev_info ( True , " " )
start_test ( " Test bpftool bound info reporting (remote ns)... " )
check_dev_info ( False , ns )
start_test ( " Test bpftool bound info reporting (back to own ns)... " )
sim . set_ns ( " " )
check_dev_info ( False , " " )
2018-01-17 19:13:31 -08:00
prog_file , _ = pin_prog ( " /sys/fs/bpf/tmp_prog " )
map_file , _ = pin_map ( " /sys/fs/bpf/tmp_map " , idx = 1 , expected = 2 )
2017-12-27 18:39:11 -08:00
sim . remove ( )
start_test ( " Test bpftool bound info reporting (removed dev)... " )
2018-01-17 19:13:31 -08:00
check_dev_info_removed ( prog_file = prog_file , map_file = map_file )
# Remove all pinned files and reinstantiate the netdev
clean_up ( )
bpftool_prog_list_wait ( expected = 0 )
sim = NetdevSim ( )
start_test ( " Test map update (no flags)... " )
sim . set_xdp ( map_obj , " offload " , JSON = False ) # map fixup msg breaks JSON
maps = bpftool_map_list ( expected = 2 )
array = maps [ 0 ] if maps [ 0 ] [ " type " ] == " array " else maps [ 1 ]
htab = maps [ 0 ] if maps [ 0 ] [ " type " ] == " hash " else maps [ 1 ]
for m in maps :
for i in range ( 2 ) :
bpftool ( " map update id %d key %s value %s " %
( m [ " id " ] , int2str ( " I " , i ) , int2str ( " Q " , i * 3 ) ) )
for m in maps :
ret , _ = bpftool ( " map update id %d key %s value %s " %
( m [ " id " ] , int2str ( " I " , 3 ) , int2str ( " Q " , 3 * 3 ) ) ,
fail = False )
fail ( ret == 0 , " added too many entries " )
start_test ( " Test map update (exists)... " )
for m in maps :
for i in range ( 2 ) :
bpftool ( " map update id %d key %s value %s exist " %
( m [ " id " ] , int2str ( " I " , i ) , int2str ( " Q " , i * 3 ) ) )
for m in maps :
ret , err = bpftool ( " map update id %d key %s value %s exist " %
( m [ " id " ] , int2str ( " I " , 3 ) , int2str ( " Q " , 3 * 3 ) ) ,
fail = False )
fail ( ret == 0 , " updated non-existing key " )
fail ( err [ " error " ] . find ( " No such file or directory " ) == - 1 ,
" expected ENOENT, error is ' %s ' " % ( err [ " error " ] ) )
start_test ( " Test map update (noexist)... " )
for m in maps :
for i in range ( 2 ) :
ret , err = bpftool ( " map update id %d key %s value %s noexist " %
( m [ " id " ] , int2str ( " I " , i ) , int2str ( " Q " , i * 3 ) ) ,
fail = False )
fail ( ret == 0 , " updated existing key " )
fail ( err [ " error " ] . find ( " File exists " ) == - 1 ,
" expected EEXIST, error is ' %s ' " % ( err [ " error " ] ) )
start_test ( " Test map dump... " )
for m in maps :
_ , entries = bpftool ( " map dump id %d " % ( m [ " id " ] ) )
for i in range ( 2 ) :
key = str2int ( entries [ i ] [ " key " ] )
fail ( key != i , " expected key %d , got %d " % ( key , i ) )
val = str2int ( entries [ i ] [ " value " ] )
fail ( val != i * 3 , " expected value %d , got %d " % ( val , i * 3 ) )
start_test ( " Test map getnext... " )
for m in maps :
_ , entry = bpftool ( " map getnext id %d " % ( m [ " id " ] ) )
key = str2int ( entry [ " next_key " ] )
fail ( key != 0 , " next key %d , expected %d " % ( key , 0 ) )
_ , entry = bpftool ( " map getnext id %d key %s " %
( m [ " id " ] , int2str ( " I " , 0 ) ) )
key = str2int ( entry [ " next_key " ] )
fail ( key != 1 , " next key %d , expected %d " % ( key , 1 ) )
ret , err = bpftool ( " map getnext id %d key %s " %
( m [ " id " ] , int2str ( " I " , 1 ) ) , fail = False )
fail ( ret == 0 , " got next key past the end of map " )
fail ( err [ " error " ] . find ( " No such file or directory " ) == - 1 ,
" expected ENOENT, error is ' %s ' " % ( err [ " error " ] ) )
start_test ( " Test map delete (htab)... " )
for i in range ( 2 ) :
bpftool ( " map delete id %d key %s " % ( htab [ " id " ] , int2str ( " I " , i ) ) )
start_test ( " Test map delete (array)... " )
for i in range ( 2 ) :
ret , err = bpftool ( " map delete id %d key %s " %
( htab [ " id " ] , int2str ( " I " , i ) ) , fail = False )
fail ( ret == 0 , " removed entry from an array " )
fail ( err [ " error " ] . find ( " No such file or directory " ) == - 1 ,
" expected ENOENT, error is ' %s ' " % ( err [ " error " ] ) )
start_test ( " Test map remove... " )
sim . unset_xdp ( " offload " )
bpftool_map_list_wait ( expected = 0 )
sim . remove ( )
sim = NetdevSim ( )
sim . set_xdp ( map_obj , " offload " , JSON = False ) # map fixup msg breaks JSON
sim . remove ( )
bpftool_map_list_wait ( expected = 0 )
start_test ( " Test map creation fail path... " )
sim = NetdevSim ( )
sim . dfs [ " bpf_map_accept " ] = " N "
ret , _ = sim . set_xdp ( map_obj , " offload " , JSON = False , fail = False )
fail ( ret == 0 ,
" netdevsim didn ' t refuse to create a map with offload disabled " )
2017-12-27 18:39:11 -08:00
2017-12-01 15:09:00 -08:00
print ( " %s : OK " % ( os . path . basename ( __file__ ) ) )
finally :
log ( " Clean up... " , " " , level = 1 )
log_level_inc ( )
clean_up ( )