forked from dushyantbehl/ebpf-client-sdk
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathebpf-sdk-cli.py
203 lines (172 loc) · 6.28 KB
/
ebpf-sdk-cli.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
##
# Simple python based bpf map CRUD tool
#
# Author: Dushyant Behl <[email protected]>
import os
import json
import argparse
import logging
import time
import yaml
import btf
logging.basicConfig(
format='%(asctime)s,%(msecs)d %(levelname)-4s [%(filename)s:%(lineno)d] %(message)s',
datefmt='%Y-%m-%d:%H:%M:%S',
level=logging.DEBUG)
log = logging.getLogger(__name__)
def execute_blocking_call(cmd, dirname=None):
if dirname is not None:
os.chdir(dirname)
log.debug("%s" % (cmd))
exitcode = os.system(cmd)
return exitcode
encoding = 'yaml'
tmpfilename='/tmp/ebpf-sdk-cli-input.'+encoding
def askUserForValues(key, value):
#key['_comment'] = 'Fill the key according to the json in the input field'
obj = {'key': key}
if value is not None:
#value['_comment'] = 'Fill the value according to the json in the input field'
obj['value'] = value
obj['byteOrder'] = "reversed"
with open(tmpfilename, 'w') as f:
# convert to yaml
obj = yaml.safe_dump(obj, indent=1, sort_keys=False)
f.write(obj)
openEditorCMD = 'vim '+tmpfilename
execute_blocking_call(openEditorCMD)
# Assume values are filled in.
f = open(tmpfilename, 'r')
content = f.read()
userProvided = yaml.safe_load(content)
#log.info('After user input - ')
#log.info(yaml.safe_dump(userProvided, indent=1, sort_keys=False))
f.close()
flatten_key = userProvided['key']
if value is not None:
flatten_value = userProvided['value']
else:
flatten_value = None
try:
byteOrder = userProvided['byteOrder']
except:
byteOrder = "reversed"
pass
return {
"flatten_key": flatten_key,
"flatten_value": flatten_value,
"byteOrder": byteOrder
}
def getUserInput(msg, expectedType=None):
dst = input(msg+'\n')
if expectedType is not None:
try:
converted = expectedType(dst)
return converted
except ValueError:
raise Exception("expectedType "+str(expectedType)+" input "+str(type(dst)))
return dst
def loadMapPaths(maps):
log.info("First we need to know where maps are")
for map in maps:
log.info("Please tell the pinned location of the map - "+map['name'])
map['path'] = getUserInput("Please tell the pinned location of the map - "+map['name'], str)
log.info("Thanks")
log.info(json.dumps(maps, indent=1))
def convertToHexByteString(bytes):
hex_string = bytes.hex()
ret = ""
i = 0
while i<len(hex_string):
b = hex_string[i:i+2]
ret += "0x"+b+" "
i+=2
return ret
def doCreateOrUpdate(map, key_bytes, value_bytes):
key = convertToHexByteString(key_bytes)
value = convertToHexByteString(value_bytes)
cmd = 'bpftool map update pinned '+map['path'] + " "
cmd += 'key '+key+' value '+value
return execute_blocking_call(cmd)
def doRead(map, key_bytes):
key = convertToHexByteString(key_bytes)
cmd = 'bpftool map lookup pinned '+map['path'] + " "
cmd += 'key '+key
return execute_blocking_call(cmd)
def doDelete(map, key_bytes):
key = convertToHexByteString(key_bytes)
cmd = 'bpftool map delete pinned '+map['path'] + " "
cmd += 'key '+key
return execute_blocking_call(cmd)
def doCRUD(op, maps, args):
m = None
if args.map is not None:
map_name = args.map
log.info('map name passed is '+map_name)
for m in maps:
if m['name'] == map_name:
map = m
if m is None:
log.info("First we need to know which map to use")
l = len(maps)
for id in range(l):
log.info(str(id)+": "+maps[id]['name'])
idToUse = getUserInput("Please enter id of the map to use - ", int)
if idToUse<0 or idToUse>l:
log.error("Uknown input try again")
os._exit(1)
map = maps[idToUse]
log.info('Selected map is '+map['name'])
flatten_key = map['key']['flatten']
if (op == 'create' or op == 'update'):
flatten_value = map['value']['flatten']
else:
flatten_value = None
userProvided = askUserForValues(key=flatten_key, value=flatten_value)
# refresh values
flatten_key = userProvided['flatten_key']
flatten_value = userProvided['flatten_value']
byteOrder = userProvided['byteOrder']
log.info('Loaded user input ')
raw_key = btf.generateRawValue(flatten_key, byteOrder=byteOrder)
raw_value = btf.generateRawValue(flatten_value, byteOrder=byteOrder)
if op == 'create' or op == 'update':
log.info('Going to perform op '+op)
ret = doCreateOrUpdate(map, key_bytes=raw_key, value_bytes=raw_value)
elif op == 'read':
ret = doRead(map, key_bytes=raw_key)
elif op == 'delete':
ret = doDelete(map, key_bytes=raw_key)
log.info('op returned %d', ret)
def main(args):
log.info("Welcome to ebpf-client-sdk-cli")
try:
#Check and open enriched btf file.
with open(args.parsed_btf, 'r') as btf_file:
btf = json.loads(btf_file.read())
maps = btf['maps']
op = args.op
if (op == 'create' or op == 'read' or
op == 'update' or op == 'delete'):
log.info(op+' map entry for map')
doCRUD(op, maps, args)
elif op == 'enrich':
loadMapPaths(maps)
log.info("added map path info. updating file")
obj = {}
obj['maps'] = maps
with open(args.parsed_btf, 'w') as btf_file:
btf_file.write(json.dumps(obj))
else:
log.error("Unknown operation")
except Exception as e:
log.error("Exception when running executor "+str(e))
raise Exception(str(e))
if __name__ == "__main__":
argparser = argparse.ArgumentParser(description='ebpf sdk cli tool')
argparser.add_argument('--parsed_btf', dest='parsed_btf', help="enriched btf file (in json format) output from bpfmap-info-extractor", required=True)
argparser.add_argument('--op', dest='op', help='enrich/create/read/update/delete', required=True)
argparser.add_argument('--map', dest='map', help='map name to use', required=False)
argparser.add_argument('--encoding', dest='encoding', help='yaml/json to use', default='yaml', required=False)
args = argparser.parse_args()
main(args)