Hi to all! Today, let's write a small script using the C API.
And so what do we need?
1) The main structure: (man netdevice)
But we need, not the whole structure!
2) Address structure (more bits/socket.h):
3) Constants for ioctl:
Now go to IDLE2:
We implement our C-structure:
We implement the MAC class:
Importantly! You can only replace the MAC address when the interface is off. Also, the MAC address must start at 02.
We handle the arguments:
The final file can be downloaded here: GIT
And so what do we need?
- Python2 + IDLE2
- C headers and Linux man
1) The main structure: (man netdevice)
C:
struct ifreq {
char ifr_name[16];
union {
struct sockaddr ifr_hwaddr;
short ifr_flags;
};
};
2) Address structure (more bits/socket.h):
C:
struct sockaddr
{
unsigned short sa_family;
char sa_data[14];
};
3) Constants for ioctl:
- more net/if.h
- IFF_UP = 0x0001
- IFF_LOOPBACK = 0x0008
- IFF_POINTTOPOINT = 0x0010
- more bits/ioctls.h
- SIOCGIFFLAGS = 0x8913
- SIOCSIFFLAGS = 0x8914
- SIOCGIFHWADDR = 0x8927
- SIOCSIFHWADDR = 0x8924
Now go to IDLE2:
Python:
import ctypes
import socket
import fcntl
import binascii
import random
import argparse
import pprint
#
digits = '0123456789ABCDEF'
We implement our C-structure:
Python:
class _sockaddr(ctypes.Structure):
_fields_ = [
('sa_family', ctypes.c_ushort),
('sa_data', ctypes.c_char * 14)
]
#
#
class _ifunion(ctypes.Union):
_anonymous_ = ('_sa',)
_fields_ = [
('ifr_flags', ctypes.c_short),
('_sa', _sockaddr)
]
#
#
class ifreq(ctypes.Structure):
_anonymous_ = ('_un',)
_fields_ = [
('ifr_name', ctypes.c_char * 16),
('_un', _ifunion)
]
We implement the MAC class:
Python:
class MAC:
def __init__(self, ifname):
self._ifname = ifname
self._socket = None
#
self._if_flags = ifreq()
self._if_flags.ifr_name = ifname
#
self._if_hwaddr = ifreq()
self._if_hwaddr.ifr_name = ifname
#
self._socket = socket.socket(
socket.AF_PACKET,
socket.SOCK_RAW,
socket.ntohs(0x3)
)
self._socket.bind((self._ifname, 0))
fcntl.ioctl(self._socket.fileno(), SIOCGIFFLAGS, self._if_flags)
#
def __del__(self):
if self._socket: self._socket.close()
#
def if_isup(self):
if bool(self._if_flags.ifr_flags & IFF_UP): return 'Up'
else: return 'Down'
#
def if_up(self):
self._if_flags.ifr_flags |= IFF_UP
fcntl.ioctl(self._socket.fileno(), SIOCSIFFLAGS, self._if_flags)
#
def if_down(self):
self._if_flags.ifr_flags ^= IFF_UP
fcntl.ioctl(self._socket.fileno(), SIOCSIFFLAGS, self._if_flags)
#
def if_check(self):
if (self._if_flags.ifr_flags & IFF_LOOPBACK): return False
if (self._if_flags.ifr_flags & IFF_POINTTOPOINT): return False
return True
#
def hwaddr_get(self):
fcntl.ioctl(self._socket.fileno(), SIOCGIFHWADDR, self._if_hwaddr)
MAC = self._if_hwaddr.sa_data
MAC = binascii.hexlify(MAC)
MAC = MAC.upper()
#
return '{}:{}:{}:{}:{}:{}'.format(
MAC[:2], MAC[2:4],
MAC[4:6], MAC[6:8],
MAC[8:10], MAC[10:12]
)
#
def hwaddr_rnd(self):
MAC = '02{}{}{}{}{}{}{}{}{}{}'.format(
random.choice(digits),
random.choice(digits),
random.choice(digits),
random.choice(digits),
random.choice(digits),
random.choice(digits),
random.choice(digits),
random.choice(digits),
random.choice(digits),
random.choice(digits)
)
#
self._if_hwaddr.sa_data = binascii.unhexlify(MAC)
self.if_down()
fcntl.ioctl(self._socket.fileno(), SIOCSIFHWADDR, self._if_hwaddr)
self.if_up()
return '{}:{}:{}:{}:{}:{}'.format(
MAC[:2], MAC[2:4],
MAC[4:6], MAC[6:8],
MAC[8:10], MAC[10:12]
)
Importantly! You can only replace the MAC address when the interface is off. Also, the MAC address must start at 02.
We handle the arguments:
Python:
args = argparse.ArgumentParser()
args.add_argument('iface')
args = args.parse_args()
#
try:
mac = MAC(args.iface)
pprint.pprint('Interface {} is {}'.format(args.iface, mac.if_isup()))
#
if mac.if_check():
pprint.pprint('OLD: {}'.format(mac.hwaddr_get()))
pprint.pprint('NEW: {}'.format(mac.hwaddr_rnd()))
#
else: raise TypeError('Interface loop or p-to-p')
#
except Exception as msg:
pprint.pprint('{}: {}'.format(msg.__class__.__name__, msg))
The final file can be downloaded here: GIT