Skip to content

Commit

Permalink
Replace netutils.py with from-scratch implementation under Apache 2.0…
Browse files Browse the repository at this point in the history
… license (#211)

* Replace netutils.py with from-scratch implementation under Apache 2.0 license

Signed-off-by: Andy Fingerhut <[email protected]>

* Reformat Python code with black package

Signed-off-by: Andy Fingerhut <[email protected]>

* Try backing CI off to Ubuntu 22.04 to see if tests pass there

Signed-off-by: Andy Fingerhut <[email protected]>

* Replace get_mac implementation with one not requiring package netifaces

Signed-off-by: Andy Fingerhut <[email protected]>

---------

Signed-off-by: Andy Fingerhut <[email protected]>
Signed-off-by: Andy Fingerhut <[email protected]>
  • Loading branch information
jafingerhut authored Feb 18, 2025
1 parent c554f83 commit 8e5e96b
Show file tree
Hide file tree
Showing 4 changed files with 75 additions and 53 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ on:
jobs:
verify-python:
name: Python code verification (src)
runs-on: ubuntu-latest
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- name: Install dependencies
Expand All @@ -26,7 +26,7 @@ jobs:
run: |
make test
test:
runs-on: ubuntu-latest
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/pypi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ jobs:
publish-to-pypi:
name: Publish a Python distribution to PyPI
if: ${{ github.repository == 'p4lang/ptf' }}
runs-on: [ubuntu-latest]
runs-on: [ubuntu-22.04]
steps:
- uses: actions/checkout@v4
- name: Set up Python
Expand Down
120 changes: 71 additions & 49 deletions src/ptf/netutils.py
Original file line number Diff line number Diff line change
@@ -1,68 +1,90 @@
"""
Network utilities for the OpenFlow test framework
"""
# Copyright 2025 Andy Fingerhut
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# SPDX-License-Identifier: Apache-2.0

###########################################################################
## ##
## Promiscuous mode enable/disable ##
## ##
## Based on code from Scapy by Phillippe Biondi ##
## ##
## ##
## This program is free software; you can redistribute it and/or modify it ##
## under the terms of the GNU General Public License as published by the ##
## Free Software Foundation; either version 2, or (at your option) any ##
## later version. ##
## ##
## This program is distributed in the hope that it will be useful, but ##
## WITHOUT ANY WARRANTY; without even the implied warranty of ##
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ##
## General Public License for more details. ##
## ##
#############################################################################
# Note: Earlier versions of this file were licensed with a copyleft
# license.
#
# This version has been rewritten from scratch, with no reference to
# the implementations in file netutils.py earlier in the commit log of
# the repository https://github.com/p4lang/ptf

######################################################################
# get_mac:
#
# The new get_mac is copied from the Apache-2.0 implementation
# in source file ptf_nn_agent.py of this repo.

######################################################################
# set_promisc:
#
# StackOverflow answer explaining one way to do it at [2].
# [2] https://stackoverflow.com/questions/6067405/python-sockets-enabling-promiscuous-mode-in-linux

######################################################################
#
# Promiscuous mode enable/disable

import ctypes
import fcntl
import socket
from fcntl import ioctl
import struct

# From net/if_arp.h
ARPHDR_ETHER = 1
ARPHDR_LOOPBACK = 772
# Constant from Linux /usr/include/linux/if.h or net/if.h
IFF_PROMISC = 0x100

# From bits/ioctls.h
# Constants from Linux bits/ioctls.h or linux/sockios.h
SIOCGIFHWADDR = 0x8927 # Get hardware address
SIOCGIFINDEX = 0x8933 # name -> if_index mapping
SIOCGIFFLAGS = 0x8913
SIOCSIFFLAGS = 0x8914

# From netpacket/packet.h
PACKET_ADD_MEMBERSHIP = 1
PACKET_DROP_MEMBERSHIP = 2
PACKET_MR_PROMISC = 1

# From bits/socket.h
SOL_PACKET = 263
class ifreq(ctypes.Structure):
_fields_ = [("ifr_ifrn", ctypes.c_char * 16), ("ifr_flags", ctypes.c_short)]


def get_if(iff, cmd):
def get_if(iff: str, cmd: int) -> bytes:
s = socket.socket()
ifreq = ioctl(s, cmd, struct.pack("16s16x", iff.encode("utf-8")))
ifreq = fcntl.ioctl(s, cmd, struct.pack("16s16x", iff.encode("utf-8")))
s.close()
return ifreq


def get_if_index(iff):
return int(struct.unpack("I", get_if(iff, SIOCGIFINDEX)[16:20])[0])


def get_mac(iff):
return ":".join(["%02x" % ord(char) for char in get_if(iff, SIOCGIFHWADDR)[18:24]])
# Given iff, the name of a network interface (e.g. 'veth0') as a
# string, return a string of the form 'xx:yy:zz:aa:bb:cc' containing
# the MAC address of that interface, where all digits are hexadecimal.
def get_mac(iff: str) -> str:
return ":".join(
["%02x" % char for char in bytearray(get_if(iff, SIOCGIFHWADDR)[18:24])]
)


# Given iff, the name of a network interface (e.g. 'veth0') as a
# string, and s, a SOCK_RAW socket bound to that interface, set the
# interface in promiscuous mode if parameter val != 0, or into
# non-promiscuous mode if val == 0.
def set_promisc(s, iff, val=1):
mreq = struct.pack(
"IHH8s", get_if_index(iff), PACKET_MR_PROMISC, 0, "".encode("utf-8")
)
if val:
cmd = PACKET_ADD_MEMBERSHIP
ifr = ifreq()
ifr.ifr_ifrn = bytes(iff, "utf-8")
# Get current interface flags
s_fileno = s.fileno()
fcntl.ioctl(s_fileno, SIOCGIFFLAGS, ifr)
orig_flags = ifr.ifr_flags
if val != 0:
ifr.ifr_flags |= IFF_PROMISC
else:
cmd = PACKET_DROP_MEMBERSHIP
s.setsockopt(SOL_PACKET, cmd, mreq)
ifr.ifr_flags &= ~IFF_PROMISC
# Set flags, if they have changed
if ifr.ifr_flags != orig_flags:
fcntl.ioctl(s_fileno, SIOCSIFFLAGS, ifr)
2 changes: 1 addition & 1 deletion src/ptf/packet.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
""" A pluggable packet module
"""A pluggable packet module
This module dynamically imports definitions from packet manipulation module,
specified in config or provided as an agrument.
Expand Down

0 comments on commit 8e5e96b

Please sign in to comment.