blob: 43bdeeb16b5ff5a325bf88837a189642b520b641 [file] [log] [blame] [edit]
# SPDX-License-Identifier: (GPL-2.0 OR Linux-OpenIB)
# Copyright (c) 2020 Nvidia. All rights reserved.
from pyverbs.pyverbs_error import PyverbsError
from libc.string cimport memcpy
import socket, struct
U32_MASK = 0xffffffff
cdef class Spec(PyverbsObject):
"""
Abstract class for all the specs to derive from.
"""
def __init__(self):
raise NotImplementedError('This class is abstract.')
@property
def size(self):
return self.size
cpdef _copy_data(self, unsigned long ptr):
"""
memcpy the spec to the provided address in proper order.
This function must be implemented in each subclass.
:param addr: address to copy spec to
"""
raise NotImplementedError('Must be implemented in subclass.')
def __str__(self):
return f"{'Spec type':<16}: {self.type_to_str(self.spec_type):<20}\n" \
f"{'Size':<16}: {self.size:<20}\n"
@staticmethod
def type_to_str(spec_type):
types = {v.IBV_FLOW_SPEC_ETH : 'IBV_FLOW_SPEC_ETH',
v.IBV_FLOW_SPEC_IPV4_EXT : "IBV_FLOW_SPEC_IPV4_EXT",
v.IBV_FLOW_SPEC_IPV6 : "IBV_FLOW_SPEC_IPV6",
v.IBV_FLOW_SPEC_TCP : "IBV_FLOW_SPEC_TCP",
v.IBV_FLOW_SPEC_UDP : "IBV_FLOW_SPEC_UDP"}
res_str = ""
if spec_type & v.IBV_FLOW_SPEC_INNER:
res_str += 'IBV_FLOW_SPEC_INNER '
try:
s_type = spec_type & ~v.IBV_FLOW_SPEC_INNER
res_str += types[s_type]
except IndexError:
raise PyverbsError(f'This type {s_type} is not implemented yet')
return res_str
@staticmethod
def _set_val_mask(default_mask, val=None, val_mask=None):
"""
If value is given without val_mask, default_mask will be returned.
:param default_mask: default mask to set if not provided
:param val: user provided value
:param val_mask: user provided mask
:return: resulting value and mask
"""
res_val = 0
res_mask = 0
if val is not None:
res_val = val
res_mask = default_mask if val_mask is None else val_mask
return res_val, res_mask
cdef class EthSpec(Spec):
MAC_LEN = 6
MAC_MASK = ('ff:' * MAC_LEN)[:-1]
ZERO_MAC = [0] * MAC_LEN
def __init__(self, dst_mac=None, dst_mac_mask=None, src_mac=None,
src_mac_mask=None, ether_type=None, ether_type_mask=None,
vlan_tag=None, vlan_tag_mask=None, is_inner=0):
"""
Initialize a EthSpec object over an underlying ibv_flow_spec_eth C
object that defines Ethernet header specifications for steering flow to
match on.
:param dst_mac: destination mac to match on (e.g. 'aa:bb:12:13:14:fe')
:param dst_mac_mask: destination mac mask (e.g. 'ff:ff:ff:ff:ff:ff')
:param src_mac: source mac to match on
:param src_mac_mask: source mac mask
:param ether_type: ethertype to match on
:param ether_type_mask: ethertype mask
:param vlan_tag: VLAN tag to match on
:param vlan_tag_mask: VLAN tag mask
:param is_inner: is inner spec
"""
self.spec_type = v.IBV_FLOW_SPEC_ETH
if is_inner:
self.spec_type |= v.IBV_FLOW_SPEC_INNER
self.size = sizeof(v.ibv_flow_spec_eth)
self.dst_mac, self.dst_mac_mask = self._set_val_mask(self.MAC_MASK,
dst_mac,
dst_mac_mask)
self.src_mac, self.src_mac_mask = self._set_val_mask(self.MAC_MASK,
src_mac,
src_mac_mask)
self.val.ether_type, self.mask.ether_type = \
map(socket.htons, self._set_val_mask(0xffff, ether_type,
ether_type_mask))
self.val.vlan_tag, self.mask.vlan_tag = \
map(socket.htons, self._set_val_mask(0xffff, vlan_tag,
vlan_tag_mask))
cdef _mac_to_str(self, unsigned char mac[6]):
s = ''
if len(mac) == 0:
return s
# Building string from array
# [0xa, 0x1b, 0x2c, 0x3c, 0x4d, 0x5e] -> "0a:1b:2c:3c:4d:5e"
for i in range(self.MAC_LEN):
s += hex(mac[i])[2:].zfill(2) + ':'
return s[:-1]
def _set_mac(self, val):
mac = EthSpec.ZERO_MAC[:]
if val:
s = val.split(':')
for i in range(self.MAC_LEN):
mac[i] = int(s[i], 16)
return mac
@property
def dst_mac(self):
return self._mac_to_str(self.val.dst_mac)
@dst_mac.setter
def dst_mac(self, val):
self.val.dst_mac = self._set_mac(val)
@property
def dst_mac_mask(self):
return self._mac_to_str(self.mask.dst_mac)
@dst_mac_mask.setter
def dst_mac_mask(self, val):
self.mask.dst_mac = self._set_mac(val)
@property
def src_mac(self):
return self._mac_to_str(self.val.src_mac)
@src_mac.setter
def src_mac(self, val):
self.val.src_mac = self._set_mac(val)
@property
def src_mac_mask(self):
return self._mac_to_str(self.mask.src_mac)
@src_mac_mask.setter
def src_mac_mask(self, val):
self.mask.src_mac = self._set_mac(val)
@property
def ether_type(self):
return socket.ntohs(self.val.ether_type)
@ether_type.setter
def ether_type(self, val):
self.val.ether_type = socket.htons(val)
@property
def ether_type_mask(self):
return socket.ntohs(self.mask.ether_type)
@ether_type_mask.setter
def ether_type_mask(self, val):
self.mask.ether_type = socket.htons(val)
@property
def vlan_tag(self):
return socket.ntohs(self.val.vlan_tag)
@vlan_tag.setter
def vlan_tag(self, val):
self.val.vlan_tag = socket.htons(val)
@property
def vlan_tag_mask(self):
return socket.ntohs(self.mask.vlan_tag)
@vlan_tag_mask.setter
def vlan_tag_mask(self, val):
self.mask.vlan_tag = socket.htons(val)
def __str__(self):
return super().__str__() + \
f"{'Src mac':<16}: {self.src_mac:<20} {self.src_mac_mask:<20}\n" \
f"{'Dst mac':<16}: {self.dst_mac:<20} {self.dst_mac_mask:<20}\n" \
f"{'Ether type':<16}: {self.val.ether_type:<20} " \
f"{self.mask.ether_type:<20}\n" \
f"{'Vlan tag':<16}: {self.val.vlan_tag:<20} " \
f"{self.mask.vlan_tag:<20}\n"
cpdef _copy_data(self, unsigned long ptr):
cdef v.ibv_flow_spec_eth eth
eth.size = self.size
eth.type = self.spec_type
eth.val = self.val
eth.mask = self.mask
memcpy(<void*>ptr, &eth, self.size)
cdef class Ipv4ExtSpec(Spec):
def __init__(self, dst_ip=None, dst_ip_mask=None, src_ip=None,
src_ip_mask=None, proto=None, proto_mask=None, tos=None,
tos_mask=None, ttl=None, ttl_mask=None, flags=None,
flags_mask=None, is_inner=False):
"""
Initialize an Ipv4ExtSpec object over an underlying ibv_flow_ipv4_ext C
object that defines IPv4 header specifications for steering flow to
match on.
:param dst_ip: Destination IP to match on (e.g. '1.2.3.4')
:param dst_ip_mask: Destination IP mask (e.g. '255.255.255.255')
:param src_ip: source IP to match on
:param src_ip_mask: Source IP mask
:param proto: Protocol to match on
:param proto_mask: Protocol mask
:param tos: Type of service to match on
:param tos_mask: Type of service mask
:param ttl: Time to live to match on
:param ttl_mask: Time to live mask
:param flags: Flags to match on
:param flags_mask: Flags mask
:param is_inner: Is inner spec
"""
self.spec_type = v.IBV_FLOW_SPEC_IPV4_EXT
if is_inner:
self.spec_type |= v.IBV_FLOW_SPEC_INNER
self.size = sizeof(v.ibv_flow_spec_ipv4_ext)
self.val.dst_ip, self.mask.dst_ip = \
map(socket.htonl, self._set_val_mask(U32_MASK,
self._str_to_ip(dst_ip),
self._str_to_ip(dst_ip_mask)))
self.val.src_ip, self.mask.src_ip = \
map(socket.htonl, self._set_val_mask(U32_MASK,
self._str_to_ip(src_ip),
self._str_to_ip(src_ip_mask)))
self.val.proto, self.mask.proto = self._set_val_mask(0xff, proto,
proto_mask)
self.val.tos, self.mask.tos = self._set_val_mask(0xff, tos, tos_mask)
self.val.ttl, self.mask.ttl = self._set_val_mask(0xff, ttl, ttl_mask)
self.val.flags, self.mask.flags = self._set_val_mask(0xff, flags,
flags_mask)
@staticmethod
def _str_to_ip(ip_str):
return None if ip_str is None else \
struct.unpack('!L', socket.inet_aton(ip_str))[0]
@staticmethod
def _ip_to_str(ip):
return socket.inet_ntoa(struct.pack('!L', ip))
@property
def dst_ip(self):
return self._ip_to_str(socket.ntohl(self.val.dst_ip))
@dst_ip.setter
def dst_ip(self, val):
self.val.dst_ip = socket.htonl(self._str_to_ip(val))
@property
def dst_ip_mask(self):
return self._ip_to_str(socket.ntohl(self.mask.dst_ip))
@dst_ip_mask.setter
def dst_ip_mask(self, val):
self.mask.dst_ip = socket.htonl(self._str_to_ip(val))
@property
def src_ip(self):
return self._ip_to_str(socket.ntohl(self.val.src_ip))
@src_ip.setter
def src_ip(self, val):
self.val.src_ip = socket.htonl(self._str_to_ip(val))
@property
def src_ip_mask(self):
return self._ip_to_str(socket.ntohl(self.mask.src_ip))
@src_ip_mask.setter
def src_ip_mask(self, val):
self.mask.src_ip = socket.htonl(self._str_to_ip(val))
@property
def proto(self):
return self.val.proto
@proto.setter
def proto(self, val):
self.val.proto = val
@property
def proto_mask(self):
return self.mask.proto
@proto_mask.setter
def proto_mask(self, val):
self.mask.proto = val
@property
def tos(self):
return self.val.tos
@tos.setter
def tos(self, val):
self.val.tos = val
@property
def tos_mask(self):
return self.mask.tos
@tos_mask.setter
def tos_mask(self, val):
self.mask.tos = val
@property
def ttl(self):
return self.val.ttl
@ttl.setter
def ttl(self, val):
self.val.ttl = val
@property
def ttl_mask(self):
return self.mask.ttl
@ttl_mask.setter
def ttl_mask(self, val):
self.mask.ttl = val
@property
def flags(self):
return self.val.flags
@flags.setter
def flags(self, val):
self.val.flags = val
@property
def flags_mask(self):
return self.mask.flags
@flags_mask.setter
def flags_mask(self, val):
self.mask.flags = val
def __str__(self):
return super().__str__() + \
f"{'Src IP':<16}: {self.src_ip:<20} {self.src_ip_mask:<20}\n" \
f"{'Dst IP':<16}: {self.dst_ip:<20} {self.dst_ip_mask:<20}\n" \
f"{'Proto':<16}: {self.val.proto:<20} {self.mask.proto:<20}\n" \
f"{'ToS':<16}: {self.val.tos:<20} {self.mask.tos:<20}\n" \
f"{'TTL':<16}: {self.val.ttl:<20} {self.mask.ttl:<20}\n" \
f"{'Flags':<16}: {self.val.flags:<20} {self.mask.flags:<20}\n"
cpdef _copy_data(self, unsigned long ptr):
cdef v.ibv_flow_spec_ipv4_ext ipv4
ipv4.size = self.size
ipv4.type = self.spec_type
ipv4.val = self.val
ipv4.mask = self.mask
memcpy(<void*>ptr, &ipv4, self.size)
cdef class TcpUdpSpec(Spec):
def __init__(self, v.ibv_flow_spec_type spec_type, dst_port=None,
dst_port_mask=None, src_port=None, src_port_mask=None,
is_inner=False):
"""
Initialize a TcpUdpSpec object over an underlying ibv_flow_tcp_udp C
object that defines TCP or UDP header specifications for steering flow
to match on.
:param spec_type: IBV_FLOW_SPEC_TCP or IBV_FLOW_SPEC_UDP
:param dst_port: Destination port to match on
:param dst_port_mask: Destination port mask
:param src_port: Source port to match on
:param src_port_mask: Source port mask
:param is_inner: Is inner spec
"""
if spec_type is not v.IBV_FLOW_SPEC_TCP and spec_type is not\
v.IBV_FLOW_SPEC_UDP:
raise PyverbsError('Spec type must be IBV_FLOW_SPEC_TCP or'
' IBV_FLOW_SPEC_UDP')
self.spec_type = spec_type
if is_inner:
self.spec_type |= v.IBV_FLOW_SPEC_INNER
self.size = sizeof(v.ibv_flow_spec_tcp_udp)
self.val.dst_port, self.mask.dst_port = \
map(socket.htons, self._set_val_mask(0xffff, dst_port,
dst_port_mask))
self.val.src_port, self.mask.src_port = \
map(socket.htons, self._set_val_mask(0xffff, src_port,
src_port_mask))
@property
def dst_port(self):
return socket.ntohs(self.val.dst_port)
@dst_port.setter
def dst_port(self, val):
self.val.dst_port = socket.htons(val)
@property
def dst_port_mask(self):
return socket.ntohs(self.mask.dst_port)
@dst_port_mask.setter
def dst_port_mask(self, val):
self.mask.dst_port = socket.htons(val)
@property
def src_port(self):
return socket.ntohs(self.val.src_port)
@src_port.setter
def src_port(self, val):
self.val.src_port = socket.htons(val)
@property
def src_port_mask(self):
return socket.ntohs(self.mask.src_port)
@src_port_mask.setter
def src_port_mask(self, val):
self.mask.src_port = socket.htons(val)
def __str__(self):
return super().__str__() + \
f"{'Src port':<16}: {self.src_port:<20} {self.src_port_mask:<20}\n" \
f"{'Dst port':<16}: {self.dst_port:<20} {self.dst_port_mask:<20}\n"
cpdef _copy_data(self, unsigned long ptr):
cdef v.ibv_flow_spec_tcp_udp tcp_udp
tcp_udp.size = self.size
tcp_udp.type = self.spec_type
tcp_udp.val = self.val
tcp_udp.mask = self.mask
memcpy(<void*>ptr, &tcp_udp, self.size)
cdef class Ipv6Spec(Spec):
EMPTY_IPV6 = '::'
IPV6_MASK = ("ffff:" * 8)[:-1]
FLOW_LABEL_MASK = 0xfffff
def __init__(self, dst_ip=None, dst_ip_mask=None, src_ip=None,
src_ip_mask=None, flow_label=None, flow_label_mask=None,
next_hdr=None, next_hdr_mask=None, traffic_class=None,
traffic_class_mask=None, hop_limit=None, hop_limit_mask=None,
is_inner=False):
"""
Initialize an Ipv6Spec object over an underlying ibv_flow_ipv6 C
object that defines IPv6 header specifications for steering flow to
match on.
:param dst_ip: Destination IPv6 to match on (e.g. 'a0a1::a2a3:a4a5:a6a7:a8a9')
:param dst_ip_mask: Destination IPv6 mask (e.g. 'ffff::ffff:ffff:ffff:ffff')
:param src_ip: Source IPv6 to match on
:param src_ip_mask: Source IPv6 mask
:param flow_label: Flow label to match on
:param flow_label_mask: Flow label mask
:param next_hdr: Next header to match on
:param next_hdr_mask: Next header mask
:param traffic_class: Traffic class to match on
:param traffic_class_mask: Traffic class mask
:param hop_limit: Hop limit to match on
:param hop_limit_mask: Hop limit mask
:param is_inner: Is inner spec
"""
self.spec_type = v.IBV_FLOW_SPEC_IPV6
if is_inner:
self.spec_type |= v.IBV_FLOW_SPEC_INNER
self.size = sizeof(v.ibv_flow_spec_ipv6)
if dst_ip is None:
def_dst_ip_mask = self.EMPTY_IPV6
dst_ip = '::'
else:
def_dst_ip_mask = self.IPV6_MASK
if src_ip is None:
def_src_ip_mask = self.EMPTY_IPV6
src_ip = '::'
else:
def_src_ip_mask = self.IPV6_MASK
self.dst_ip, self.dst_ip_mask = self._set_val_mask(def_dst_ip_mask,
dst_ip, dst_ip_mask)
self.src_ip, self.src_ip_mask = self._set_val_mask(def_src_ip_mask,
src_ip, src_ip_mask)
self.val.flow_label, self.mask.flow_label = \
map(socket.htonl, self._set_val_mask(self.FLOW_LABEL_MASK,
flow_label, flow_label_mask))
self.val.next_hdr, self.mask.next_hdr = \
self._set_val_mask(0xff, next_hdr, next_hdr_mask)
self.val.traffic_class, self.mask.traffic_class = \
self._set_val_mask(0xff, traffic_class, traffic_class_mask)
self.val.hop_limit, self.mask.hop_limit = \
self._set_val_mask(0xff, hop_limit, hop_limit_mask)
@property
def dst_ip(self):
return socket.inet_ntop(socket.AF_INET6, self.val.dst_ip[:16])
@dst_ip.setter
def dst_ip(self, val):
self.val.dst_ip = socket.inet_pton(socket.AF_INET6, val)
@property
def dst_ip_mask(self):
return socket.inet_ntop(socket.AF_INET6, self.mask.dst_ip[:16])
@dst_ip_mask.setter
def dst_ip_mask(self, val):
self.mask.dst_ip = socket.inet_pton(socket.AF_INET6, val)
@property
def src_ip(self):
return socket.inet_ntop(socket.AF_INET6, self.val.src_ip[:16])
@src_ip.setter
def src_ip(self, val):
self.val.src_ip = socket.inet_pton(socket.AF_INET6, val)
@property
def src_ip_mask(self):
return socket.inet_ntop(socket.AF_INET6, self.mask.src_ip[:16])
@src_ip_mask.setter
def src_ip_mask(self, val):
self.mask.src_ip = socket.inet_pton(socket.AF_INET6, val)
@property
def flow_label(self):
return socket.ntohl(self.val.flow_label)
@flow_label.setter
def flow_label(self, val):
self.val.flow_label = socket.htonl(val)
@property
def flow_label_mask(self):
return socket.ntohl(self.mask.flow_label)
@flow_label_mask.setter
def flow_label_mask(self, val):
self.mask.flow_label = socket.htonl(val)
@property
def next_hdr(self):
return self.val.next_hdr
@next_hdr.setter
def next_hdr(self, val):
self.val.next_hdr = val
@property
def next_hdr_mask(self):
return self.mask.next_hdr
@next_hdr_mask.setter
def next_hdr_mask(self, val):
self.mask.next_hdr = val
@property
def traffic_class(self):
return self.val.traffic_class
@traffic_class.setter
def traffic_class(self, val):
self.val.traffic_class = val
@property
def traffic_class_mask(self):
return self.mask.traffic_class
@traffic_class_mask.setter
def traffic_class_mask(self, val):
self.mask.traffic_class = val
@property
def hop_limit(self):
return self.val.hop_limit
@hop_limit.setter
def hop_limit(self, val):
self.val.hop_limit = val
@property
def hop_limit_mask(self):
return self.mask.hop_limit
@hop_limit_mask.setter
def hop_limit_mask(self, val):
self.mask.hop_limit = val
def __str__(self):
return super().__str__() + \
f"{'Src IP':<16}: {self.src_ip:<20} {self.src_ip_mask:<20}\n" \
f"{'Dst IP':<16}: {self.dst_ip:<20} {self.dst_ip_mask:<20}\n" \
f"{'Flow label':<16}: {self.flow_label:<20} {self.flow_label_mask:<20}\n" \
f"{'Next header':<16}: {self.next_hdr:<20} {self.next_hdr_mask:<20}\n" \
f"{'Traffic class':<16}: {self.traffic_class:<20} {self.traffic_class_mask:<20}\n" \
f"{'Hop limit':<16}: {self.hop_limit:<20} {self.hop_limit_mask:<20}\n"
cpdef _copy_data(self, unsigned long ptr):
cdef v.ibv_flow_spec_ipv6 ipv6
ipv6.size = self.size
ipv6.type = self.spec_type
ipv6.val = self.val
ipv6.mask = self.mask
memcpy(<void*>ptr, &ipv6, self.size)