2017-12-02 02:09:00 +03: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-28 05:39:11 +03:00
import random
import string
2018-01-18 06:13:31 +03:00
import struct
2017-12-02 02:09:00 +03:00
import subprocess
import time
2019-02-06 07:03:22 +03:00
import traceback
2017-12-02 02:09:00 +03:00
logfile = None
log_level = 1
2018-01-23 22:22:53 +03:00
skip_extack = False
2017-12-02 02:09:00 +03: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-28 05:39:11 +03:00
netns = [ ] # net namespaces to be removed
2017-12-02 02:09:00 +03: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 )
2019-02-06 07:03:22 +03:00
tb = " " . join ( traceback . extract_stack ( ) . format ( ) )
print ( tb )
log ( " FAIL: " + msg , tb , level = 1 )
2017-12-02 02:09:00 +03:00
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 22:22:53 +03:00
def tool ( name , args , flags , JSON = True , ns = " " , fail = True , include_stderr = False ) :
2017-12-02 02:09:00 +03:00
params = " "
if JSON :
params + = " %s " % ( flags [ " json " ] )
2017-12-28 05:39:11 +03:00
if ns != " " :
ns = " ip netns exec %s " % ( ns )
2018-01-23 22:22:53 +03: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-02 02:09:00 +03:00
else :
return ret , out
2018-07-17 20:53:29 +03:00
def bpftool ( args , JSON = True , ns = " " , fail = True , include_stderr = False ) :
return tool ( " bpftool " , args , { " json " : " -p " } , JSON = JSON , ns = ns ,
fail = fail , include_stderr = include_stderr )
2017-12-02 02:09:00 +03:00
2017-12-28 05:39:11 +03:00
def bpftool_prog_list ( expected = None , ns = " " ) :
_ , progs = bpftool ( " prog show " , JSON = True , ns = ns , fail = True )
2018-06-14 21:06:56 +03:00
# Remove the base progs
for p in base_progs :
if p in progs :
progs . remove ( p )
2017-12-02 02:09:00 +03: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-18 06:13:31 +03:00
def bpftool_map_list ( expected = None , ns = " " ) :
_ , maps = bpftool ( " map show " , JSON = True , ns = ns , fail = True )
2018-06-14 21:06:56 +03:00
# Remove the base maps
for m in base_maps :
if m in maps :
maps . remove ( m )
2018-01-18 06:13:31 +03:00
if expected is not None :
if len ( maps ) != expected :
fail ( True , " %d BPF maps loaded, expected %d " %
( len ( maps ) , expected ) )
return maps
2017-12-02 02:09:00 +03: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-18 06:13:31 +03: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-07-17 20:53:29 +03:00
def bpftool_prog_load ( sample , file_name , maps = [ ] , prog_type = " xdp " , dev = None ,
fail = True , include_stderr = False ) :
args = " prog load %s %s " % ( os . path . join ( bpf_test_dir , sample ) , file_name )
if prog_type is not None :
args + = " type " + prog_type
if dev is not None :
args + = " dev " + dev
if len ( maps ) :
args + = " map " + " map " . join ( maps )
res = bpftool ( args , fail = fail , include_stderr = include_stderr )
if res [ 0 ] == 0 :
files . append ( file_name )
return res
2018-01-23 22:22:53 +03:00
def ip ( args , force = False , JSON = True , ns = " " , fail = True , include_stderr = False ) :
2017-12-02 02:09:00 +03:00
if force :
args = " -force " + args
2018-01-23 22:22:53 +03:00
return tool ( " ip " , args , { " json " : " -j " } , JSON = JSON , ns = ns ,
fail = fail , include_stderr = include_stderr )
2017-12-02 02:09:00 +03:00
2018-01-23 22:22:53 +03: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-02 02:09:00 +03: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-28 05:39:11 +03: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-18 06:13:31 +03: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-02 02:09:00 +03: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 .
"""
2018-07-17 20:53:29 +03:00
def __init__ ( self , link = None ) :
self . link = link
2017-12-02 02:09:00 +03:00
self . dev = self . _netdevsim_create ( )
devs . append ( self )
2017-12-28 05:39:11 +03:00
self . ns = " "
2017-12-02 02:09:00 +03:00
self . dfs_dir = ' /sys/kernel/debug/netdevsim/ %s ' % ( self . dev [ ' ifname ' ] )
2018-07-17 20:53:21 +03:00
self . sdev_dir = self . dfs_dir + ' /sdev/ '
2017-12-02 02:09:00 +03:00
self . dfs_refresh ( )
def __getitem__ ( self , key ) :
return self . dev [ key ]
def _netdevsim_create ( self ) :
2018-07-17 20:53:29 +03:00
link = " " if self . link is None else " link " + self . link . dev [ ' ifname ' ]
2017-12-02 02:09:00 +03:00
_ , old = ip ( " link show " )
2018-07-17 20:53:29 +03:00
ip ( " link add sim %d {link} type netdevsim " . format ( link = link ) )
2017-12-02 02:09:00 +03:00
_ , 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-28 05:39:11 +03:00
ip ( " link del dev %s " % ( self . dev [ " ifname " ] ) , ns = self . ns )
2017-12-02 02:09:00 +03:00
def dfs_refresh ( self ) :
self . dfs = DebugfsDir ( self . dfs_dir )
return self . dfs
2018-07-12 06:36:43 +03:00
def dfs_read ( self , f ) :
path = os . path . join ( self . dfs_dir , f )
_ , data = cmd ( ' cat %s ' % ( path ) )
return data . strip ( )
2017-12-02 02:09:00 +03:00
def dfs_num_bound_progs ( self ) :
2018-07-17 20:53:21 +03:00
path = os . path . join ( self . sdev_dir , " bpf_bound_progs " )
2017-12-02 02:09:00 +03:00
_ , progs = cmd ( ' ls %s ' % ( path ) )
return len ( progs . split ( ) )
def dfs_get_bound_progs ( self , expected ) :
2018-07-17 20:53:21 +03:00
progs = DebugfsDir ( os . path . join ( self . sdev_dir , " bpf_bound_progs " ) )
2017-12-02 02:09:00 +03:00
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-28 05:39:11 +03: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-02 02:09:00 +03: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 22:22:55 +03:00
def set_xdp ( self , bpf , mode , force = False , JSON = True , verbose = False ,
2018-01-23 22:22:53 +03:00
fail = True , include_stderr = False ) :
2018-01-23 22:22:55 +03:00
if verbose :
bpf + = " verbose "
2017-12-02 02:09:00 +03:00
return ip ( " link set dev %s xdp %s %s " % ( self . dev [ " ifname " ] , mode , bpf ) ,
2018-01-23 22:22:53 +03:00
force = force , JSON = JSON ,
fail = fail , include_stderr = include_stderr )
2017-12-02 02:09:00 +03:00
2018-01-23 22:22:53 +03:00
def unset_xdp ( self , mode , force = False , JSON = True ,
fail = True , include_stderr = False ) :
2017-12-02 02:09:00 +03:00
return ip ( " link set dev %s xdp %s off " % ( self . dev [ " ifname " ] , mode ) ,
2018-01-23 22:22:53 +03:00
force = force , JSON = JSON ,
fail = fail , include_stderr = include_stderr )
2017-12-02 02:09:00 +03: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 22:22:56 +03:00
def cls_filter_op ( self , op , qdisc = " ingress " , prio = None , handle = None ,
2018-01-26 01:00:53 +03:00
chain = None , cls = " " , params = " " ,
2018-01-23 22:22:56 +03:00
fail = True , include_stderr = False ) :
spec = " "
if prio is not None :
spec + = " prio %d " % ( prio )
if handle :
spec + = " handle %s " % ( handle )
2018-01-26 01:00:53 +03:00
if chain is not None :
spec + = " chain %d " % ( chain )
2018-01-23 22:22:56 +03:00
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 ,
2018-01-26 01:00:53 +03:00
chain = None , da = False , verbose = False ,
2018-01-23 22:22:56 +03:00
skip_sw = False , skip_hw = False ,
fail = True , include_stderr = False ) :
cls = " bpf " + bpf
2017-12-02 02:09:00 +03:00
params = " "
if da :
params + = " da "
2018-01-23 22:22:55 +03:00
if verbose :
params + = " verbose "
2017-12-02 02:09:00 +03:00
if skip_sw :
params + = " skip_sw "
if skip_hw :
params + = " skip_hw "
2018-01-23 22:22:56 +03:00
return self . cls_filter_op ( op = op , prio = prio , handle = handle , cls = cls ,
2018-01-26 01:00:53 +03:00
chain = chain , params = params ,
2018-01-23 22:22:56 +03:00
fail = fail , include_stderr = include_stderr )
2017-12-02 02:09:00 +03: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-18 06:13:31 +03:00
global files , netns , devs
2017-12-02 02:09:00 +03:00
for dev in devs :
dev . remove ( )
for f in files :
cmd ( " rm -f %s " % ( f ) )
2017-12-28 05:39:11 +03:00
for ns in netns :
cmd ( " ip netns delete %s " % ( ns ) )
2018-01-18 06:13:31 +03:00
files = [ ]
netns = [ ]
2017-12-02 02:09:00 +03: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-18 06:13:31 +03: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-28 05:39:11 +03: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-18 06:13:31 +03:00
if not other_ns :
2017-12-28 05:39:11 +03: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-18 06:13:31 +03: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-28 05:39:11 +03:00
2018-01-23 22:22:53 +03:00
def check_extack ( output , reference , args ) :
if skip_extack :
return
lines = output . split ( " \n " )
2018-07-11 00:42:56 +03:00
comp = len ( lines ) > = 2 and lines [ 1 ] == ' Error: ' + reference
2018-01-23 22:22:53 +03:00
fail ( not comp , " Missing or incorrect netlink extack message " )
def check_extack_nsim ( output , reference , args ) :
2018-07-11 00:42:56 +03:00
check_extack ( output , " netdevsim: " + reference , args )
2018-01-23 22:22:53 +03:00
2018-01-26 01:00:52 +03:00
def check_no_extack ( res , needle ) :
fail ( ( res [ 1 ] + res [ 2 ] ) . count ( needle ) or ( res [ 1 ] + res [ 2 ] ) . count ( " Warning: " ) ,
" Found ' %s ' in command output, leaky extack? " % ( needle ) )
2018-01-23 22:22:55 +03: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 " )
2018-01-26 01:00:52 +03:00
def test_spurios_extack ( sim , obj , skip_hw , needle ) :
res = sim . cls_bpf_add_filter ( obj , prio = 1 , handle = 1 , skip_hw = skip_hw ,
include_stderr = True )
check_no_extack ( res , needle )
res = sim . cls_bpf_add_filter ( obj , op = " replace " , prio = 1 , handle = 1 ,
skip_hw = skip_hw , include_stderr = True )
check_no_extack ( res , needle )
res = sim . cls_filter_op ( op = " delete " , prio = 1 , handle = 1 , cls = " bpf " ,
include_stderr = True )
check_no_extack ( res , needle )
2019-02-06 07:03:23 +03:00
def test_multi_prog ( sim , obj , modename , modeid ) :
start_test ( " Test multi-attachment XDP - %s + offload... " %
( modename or " default " , ) )
sim . set_xdp ( obj , " offload " )
xdp = sim . ip_link_show ( xdp = True ) [ " xdp " ]
offloaded = sim . dfs_read ( " bpf_offloaded_id " )
fail ( " prog " not in xdp , " Base program not reported in single program mode " )
fail ( len ( xdp [ " attached " ] ) != 1 ,
" Wrong attached program count with one program " )
sim . set_xdp ( obj , modename )
two_xdps = sim . ip_link_show ( xdp = True ) [ " xdp " ]
offloaded2 = sim . dfs_read ( " bpf_offloaded_id " )
fail ( two_xdps [ " mode " ] != 4 , " Bad mode reported with multiple programs " )
fail ( " prog " in two_xdps , " Base program reported in multi program mode " )
fail ( xdp [ " attached " ] [ 0 ] not in two_xdps [ " attached " ] ,
" Offload program not reported after other activated " )
fail ( len ( two_xdps [ " attached " ] ) != 2 ,
" Wrong attached program count with two programs " )
fail ( two_xdps [ " attached " ] [ 0 ] [ " prog " ] [ " id " ] ==
two_xdps [ " attached " ] [ 1 ] [ " prog " ] [ " id " ] ,
" Offloaded and other programs have the same id " )
fail ( offloaded != offloaded2 ,
" Offload ID changed after loading other program " )
start_test ( " Test multi-attachment XDP - replace... " )
ret , _ , err = sim . set_xdp ( obj , " offload " , fail = False , include_stderr = True )
fail ( ret == 0 , " Replaced one of programs without -force " )
check_extack ( err , " XDP program already attached. " , args )
if modename == " " or modename == " drv " :
othermode = " " if modename == " drv " else " drv "
start_test ( " Test multi-attachment XDP - detach... " )
ret , _ , err = sim . unset_xdp ( othermode , force = True ,
fail = False , include_stderr = True )
fail ( ret == 0 , " Removed program with a bad mode " )
check_extack ( err , " program loaded with different flags. " , args )
sim . unset_xdp ( " offload " )
xdp = sim . ip_link_show ( xdp = True ) [ " xdp " ]
offloaded = sim . dfs_read ( " bpf_offloaded_id " )
fail ( xdp [ " mode " ] != modeid , " Bad mode reported after multiple programs " )
fail ( " prog " not in xdp ,
" Base program not reported after multi program mode " )
fail ( xdp [ " attached " ] [ 0 ] not in two_xdps [ " attached " ] ,
" Offload program not reported after other activated " )
fail ( len ( xdp [ " attached " ] ) != 1 ,
" Wrong attached program count with remaining programs " )
fail ( offloaded != " 0 " , " Offload ID reported with only other program left " )
start_test ( " Test multi-attachment XDP - device remove... " )
sim . set_xdp ( obj , " offload " )
sim . remove ( )
sim = NetdevSim ( )
sim . set_ethtool_tc_offloads ( True )
return sim
2018-01-26 01:00:52 +03:00
2017-12-02 02:09:00 +03: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 " )
2018-06-14 21:06:56 +03:00
base_progs = progs
_ , base_maps = bpftool ( " map " )
2017-12-02 02:09:00 +03:00
# 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-18 06:13:31 +03:00
samples = [ " sample_ret0.o " , " sample_map_ret0.o " ]
2017-12-02 02:09:00 +03: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 22:22:53 +03: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-28 05:39:11 +03: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-02 02:09:00 +03: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 22:22:53 +03:00
ret , _ , err = sim . cls_bpf_add_filter ( obj , skip_sw = True ,
fail = False , include_stderr = True )
2017-12-02 02:09:00 +03:00
fail ( ret == 0 , " TC filter loaded without enabling TC offloads " )
2018-07-11 00:42:56 +03:00
check_extack ( err , " TC offload is disabled on net device. " , args )
2017-12-02 02:09:00 +03: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 22:22:53 +03:00
ret , _ , err = sim . cls_bpf_add_filter ( bytecode , skip_sw = True ,
fail = False , include_stderr = True )
2017-12-02 02:09:00 +03:00
fail ( ret == 0 , " TC bytecode loaded for offload " )
2018-01-23 22:22:53 +03:00
check_extack_nsim ( err , " netdevsim configured to reject unbound programs. " ,
args )
2017-12-02 02:09:00 +03:00
sim . wait_for_flush ( )
2018-01-26 01:00:53 +03:00
start_test ( " Test non-0 chain offload... " )
ret , _ , err = sim . cls_bpf_add_filter ( obj , chain = 1 , prio = 1 , handle = 1 ,
skip_sw = True ,
fail = False , include_stderr = True )
fail ( ret == 0 , " Offloaded a filter to chain other than 0 " )
2018-07-11 00:42:56 +03:00
check_extack ( err , " Driver supports only offload of chain 0. " , args )
2018-01-26 01:00:53 +03:00
sim . tc_flush_filters ( )
2018-01-23 22:22:56 +03: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 " )
2018-01-26 01:00:52 +03:00
start_test ( " Test spurious extack from the driver... " )
test_spurios_extack ( sim , obj , False , " netdevsim " )
test_spurios_extack ( sim , obj , True , " netdevsim " )
sim . set_ethtool_tc_offloads ( False )
test_spurios_extack ( sim , obj , False , " TC offload is disabled " )
test_spurios_extack ( sim , obj , True , " TC offload is disabled " )
sim . set_ethtool_tc_offloads ( True )
2018-01-23 22:22:56 +03:00
sim . tc_flush_filters ( )
2017-12-02 02:09:00 +03:00
start_test ( " Test TC offloads work... " )
2018-01-23 22:22:55 +03:00
ret , _ , err = sim . cls_bpf_add_filter ( obj , verbose = True , skip_sw = True ,
fail = False , include_stderr = True )
2017-12-02 02:09:00 +03:00
fail ( ret != 0 , " TC filter did not load with TC offloads enabled " )
2018-01-23 22:22:55 +03:00
check_verifier_log ( err , " [netdevsim] Hello from netdevsim! " )
2017-12-02 02:09:00 +03: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-28 05:39:11 +03:00
fail ( " dev " in progs [ 0 ] . keys ( ) ,
" Device parameters reported for non-offloaded program " )
2017-12-02 02:09:00 +03:00
start_test ( " Test XDP prog replace with bad flags... " )
2018-07-12 06:36:43 +03:00
ret , _ , err = sim . set_xdp ( obj , " generic " , force = True ,
fail = False , include_stderr = True )
fail ( ret == 0 , " Replaced XDP program with a program in different mode " )
2019-02-06 07:03:20 +03:00
check_extack ( err ,
" native and generic XDP can ' t be active at the same time. " ,
args )
2018-01-23 22:22:53 +03:00
ret , _ , err = sim . set_xdp ( obj , " " , force = True ,
fail = False , include_stderr = True )
2017-12-02 02:09:00 +03:00
fail ( ret == 0 , " Replaced XDP program with a program in different mode " )
2018-07-12 06:36:40 +03:00
check_extack ( err , " program loaded with different flags. " , args )
2017-12-02 02:09:00 +03:00
start_test ( " Test XDP prog remove with bad flags... " )
2018-01-23 22:22:53 +03:00
ret , _ , err = sim . unset_xdp ( " " , force = True ,
fail = False , include_stderr = True )
2018-07-11 00:42:55 +03:00
fail ( ret == 0 , " Removed program with a bad mode " )
2018-07-12 06:36:40 +03:00
check_extack ( err , " program loaded with different flags. " , args )
2017-12-02 02:09:00 +03: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 22:22:53 +03:00
ret , _ , err = sim . set_xdp ( obj , " drv " , fail = False , include_stderr = True )
2017-12-02 02:09:00 +03:00
fail ( ret == 0 , " Driver should refuse to load program with MTU of 9000... " )
2018-01-23 22:22:53 +03:00
check_extack_nsim ( err , " MTU too large w/ XDP enabled. " , args )
2017-12-02 02:09:00 +03:00
sim . set_mtu ( 1500 )
sim . wait_for_flush ( )
2018-07-17 20:53:29 +03:00
start_test ( " Test non-offload XDP attaching to HW... " )
bpftool_prog_load ( " sample_ret0.o " , " /sys/fs/bpf/nooffload " )
nooffload = bpf_pinned ( " /sys/fs/bpf/nooffload " )
ret , _ , err = sim . set_xdp ( nooffload , " offload " ,
fail = False , include_stderr = True )
fail ( ret == 0 , " attached non-offloaded XDP program to HW " )
check_extack_nsim ( err , " xdpoffload of non-bound program. " , args )
rm ( " /sys/fs/bpf/nooffload " )
start_test ( " Test offload XDP attaching to drv... " )
bpftool_prog_load ( " sample_ret0.o " , " /sys/fs/bpf/offload " ,
dev = sim [ ' ifname ' ] )
offload = bpf_pinned ( " /sys/fs/bpf/offload " )
ret , _ , err = sim . set_xdp ( offload , " drv " , fail = False , include_stderr = True )
fail ( ret == 0 , " attached offloaded XDP program to drv " )
check_extack ( err , " using device-bound program without HW_MODE flag is not supported. " , args )
rm ( " /sys/fs/bpf/offload " )
sim . wait_for_flush ( )
2017-12-02 02:09:00 +03:00
start_test ( " Test XDP offload... " )
2018-01-23 22:22:55 +03:00
_ , _ , err = sim . set_xdp ( obj , " offload " , verbose = True , include_stderr = True )
2017-12-02 02:09:00 +03: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 22:22:55 +03:00
check_verifier_log ( err , " [netdevsim] Hello from netdevsim! " )
2017-12-02 02:09:00 +03: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 22:22:53 +03:00
ret , _ , err = sim . set_xdp ( pinned , " offload " ,
fail = False , include_stderr = True )
2017-12-02 02:09:00 +03:00
fail ( ret == 0 , " Pinned program loaded for a different device accepted " )
2018-01-23 22:22:53 +03:00
check_extack_nsim ( err , " program bound to different dev. " , args )
2017-12-02 02:09:00 +03:00
sim2 . remove ( )
2018-01-23 22:22:53 +03:00
ret , _ , err = sim . set_xdp ( pinned , " offload " ,
fail = False , include_stderr = True )
2017-12-02 02:09:00 +03:00
fail ( ret == 0 , " Pinned program loaded for a removed device accepted " )
2018-01-23 22:22:53 +03:00
check_extack_nsim ( err , " xdpoffload of non-bound program. " , args )
2017-12-02 02:09:00 +03:00
rm ( pin_file )
bpftool_prog_list_wait ( expected = 0 )
2019-02-06 07:03:23 +03:00
sim = test_multi_prog ( sim , obj , " " , 1 )
sim = test_multi_prog ( sim , obj , " drv " , 1 )
sim = test_multi_prog ( sim , obj , " generic " , 2 )
2018-07-12 06:36:43 +03:00
2017-12-02 02:09:00 +03:00
start_test ( " Test mixing of TC and XDP... " )
sim . tc_add_ingress ( )
sim . set_xdp ( obj , " offload " )
2018-01-23 22:22:53 +03:00
ret , _ , err = sim . cls_bpf_add_filter ( obj , skip_sw = True ,
fail = False , include_stderr = True )
2017-12-02 02:09:00 +03:00
fail ( ret == 0 , " Loading TC when XDP active should fail " )
2018-01-23 22:22:53 +03:00
check_extack_nsim ( err , " driver and netdev offload states mismatch. " , args )
2017-12-02 02:09:00 +03:00
sim . unset_xdp ( " offload " )
sim . wait_for_flush ( )
sim . cls_bpf_add_filter ( obj , skip_sw = True )
2018-01-23 22:22:53 +03:00
ret , _ , err = sim . set_xdp ( obj , " offload " , fail = False , include_stderr = True )
2017-12-02 02:09:00 +03:00
fail ( ret == 0 , " Loading XDP when TC active should fail " )
2018-01-23 22:22:53 +03:00
check_extack_nsim ( err , " TC program is already loaded. " , args )
2017-12-02 02:09:00 +03: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 22:22:53 +03:00
ret , _ , err = sim . cls_bpf_add_filter ( obj , da = True , skip_sw = True ,
fail = False , include_stderr = True )
2017-12-22 19:16:31 +03:00
fail ( ret == 0 , " Managed to offload two TC filters at the same time " )
2018-01-23 22:22:53 +03:00
check_extack_nsim ( err , " driver and netdev offload states mismatch. " , args )
2017-12-02 02:09:00 +03: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-28 05:39:11 +03:00
# Remove all pinned files and reinstantiate the netdev
clean_up ( )
bpftool_prog_list_wait ( expected = 0 )
sim = NetdevSim ( )
2018-01-18 06:13:31 +03: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-28 05:39:11 +03: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-18 06:13:31 +03: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-28 05:39:11 +03:00
sim . remove ( )
start_test ( " Test bpftool bound info reporting (removed dev)... " )
2018-01-18 06:13:31 +03: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-28 05:39:11 +03:00
2018-07-17 20:53:29 +03:00
sim . remove ( )
start_test ( " Test multi-dev ASIC program reuse... " )
simA = NetdevSim ( )
simB1 = NetdevSim ( )
simB2 = NetdevSim ( link = simB1 )
simB3 = NetdevSim ( link = simB1 )
sims = ( simA , simB1 , simB2 , simB3 )
simB = ( simB1 , simB2 , simB3 )
bpftool_prog_load ( " sample_map_ret0.o " , " /sys/fs/bpf/nsimA " ,
dev = simA [ ' ifname ' ] )
progA = bpf_pinned ( " /sys/fs/bpf/nsimA " )
bpftool_prog_load ( " sample_map_ret0.o " , " /sys/fs/bpf/nsimB " ,
dev = simB1 [ ' ifname ' ] )
progB = bpf_pinned ( " /sys/fs/bpf/nsimB " )
simA . set_xdp ( progA , " offload " , JSON = False )
for d in simB :
d . set_xdp ( progB , " offload " , JSON = False )
start_test ( " Test multi-dev ASIC cross-dev replace... " )
ret , _ = simA . set_xdp ( progB , " offload " , force = True , JSON = False , fail = False )
fail ( ret == 0 , " cross-ASIC program allowed " )
for d in simB :
ret , _ = d . set_xdp ( progA , " offload " , force = True , JSON = False , fail = False )
fail ( ret == 0 , " cross-ASIC program allowed " )
start_test ( " Test multi-dev ASIC cross-dev install... " )
for d in sims :
d . unset_xdp ( " offload " )
ret , _ , err = simA . set_xdp ( progB , " offload " , force = True , JSON = False ,
fail = False , include_stderr = True )
fail ( ret == 0 , " cross-ASIC program allowed " )
check_extack_nsim ( err , " program bound to different dev. " , args )
for d in simB :
ret , _ , err = d . set_xdp ( progA , " offload " , force = True , JSON = False ,
fail = False , include_stderr = True )
fail ( ret == 0 , " cross-ASIC program allowed " )
check_extack_nsim ( err , " program bound to different dev. " , args )
start_test ( " Test multi-dev ASIC cross-dev map reuse... " )
mapA = bpftool ( " prog show %s " % ( progA ) ) [ 1 ] [ " map_ids " ] [ 0 ]
mapB = bpftool ( " prog show %s " % ( progB ) ) [ 1 ] [ " map_ids " ] [ 0 ]
ret , _ = bpftool_prog_load ( " sample_map_ret0.o " , " /sys/fs/bpf/nsimB_ " ,
dev = simB3 [ ' ifname ' ] ,
maps = [ " idx 0 id %d " % ( mapB ) ] ,
fail = False )
fail ( ret != 0 , " couldn ' t reuse a map on the same ASIC " )
rm ( " /sys/fs/bpf/nsimB_ " )
ret , _ , err = bpftool_prog_load ( " sample_map_ret0.o " , " /sys/fs/bpf/nsimA_ " ,
dev = simA [ ' ifname ' ] ,
maps = [ " idx 0 id %d " % ( mapB ) ] ,
fail = False , include_stderr = True )
fail ( ret == 0 , " could reuse a map on a different ASIC " )
fail ( err . count ( " offload device mismatch between prog and map " ) == 0 ,
" error message missing for cross-ASIC map " )
ret , _ , err = bpftool_prog_load ( " sample_map_ret0.o " , " /sys/fs/bpf/nsimB_ " ,
dev = simB1 [ ' ifname ' ] ,
maps = [ " idx 0 id %d " % ( mapA ) ] ,
fail = False , include_stderr = True )
fail ( ret == 0 , " could reuse a map on a different ASIC " )
fail ( err . count ( " offload device mismatch between prog and map " ) == 0 ,
" error message missing for cross-ASIC map " )
start_test ( " Test multi-dev ASIC cross-dev destruction... " )
bpftool_prog_list_wait ( expected = 2 )
simA . remove ( )
bpftool_prog_list_wait ( expected = 1 )
ifnameB = bpftool ( " prog show %s " % ( progB ) ) [ 1 ] [ " dev " ] [ " ifname " ]
fail ( ifnameB != simB1 [ ' ifname ' ] , " program not bound to originial device " )
simB1 . remove ( )
bpftool_prog_list_wait ( expected = 1 )
start_test ( " Test multi-dev ASIC cross-dev destruction - move... " )
ifnameB = bpftool ( " prog show %s " % ( progB ) ) [ 1 ] [ " dev " ] [ " ifname " ]
fail ( ifnameB not in ( simB2 [ ' ifname ' ] , simB3 [ ' ifname ' ] ) ,
" program not bound to remaining devices " )
simB2 . remove ( )
ifnameB = bpftool ( " prog show %s " % ( progB ) ) [ 1 ] [ " dev " ] [ " ifname " ]
fail ( ifnameB != simB3 [ ' ifname ' ] , " program not bound to remaining device " )
simB3 . remove ( )
bpftool_prog_list_wait ( expected = 0 )
start_test ( " Test multi-dev ASIC cross-dev destruction - orphaned... " )
ret , out = bpftool ( " prog show %s " % ( progB ) , fail = False )
fail ( ret == 0 , " got information about orphaned program " )
fail ( " error " not in out , " no error reported for get info on orphaned " )
fail ( out [ " error " ] != " can ' t get prog info: No such device " ,
" wrong error for get info on orphaned " )
2017-12-02 02:09:00 +03:00
print ( " %s : OK " % ( os . path . basename ( __file__ ) ) )
finally :
log ( " Clean up... " , " " , level = 1 )
log_level_inc ( )
clean_up ( )