MAC address generator in python - python

For purpose of creating bulk virtual machines, I need to create a random MAC address generator in Python. I don't know how to generate random MAC addresses.
Is the following program correct or not?
import random
# The first line is defined for specified vendor
mac = [ 0x00, 0x24, 0x81,
random.randint(0x00, 0x7f),
random.randint(0x00, 0xff),
random.randint(0x00, 0xff) ]
print ':'.join(map(lambda x: "%02x" % x, mac))

For anyone wanting to generate their own MAC addresses (a good example is for VM NICs), you probably just want this:
"02:00:00:%02x:%02x:%02x" % (random.randint(0, 255),
random.randint(0, 255),
random.randint(0, 255))
Or, if you want to do this in a unix'y shell, this works on many:
printf '02:00:00:%02X:%02X:%02X\n' $((RANDOM%256)) $((RANDOM%256)) $((RANDOM%256))
This gives you a unicast MAC address that is 100% safe to use in your environment, and isn't trampling on anyone's registered MAC address space.
More detail...
The bottom two bits of the top byte (0x02) give you a locally administered unicast address, which is probably what you want if you are hitting stackoverflow for how to generate this. :)
If the MAC address is not locally administered, it means it is supposed to be "globally unique". MAC addresses in this category are centrally registered with the IEEE, and you should have a unique OUI (Organizationally Unique Identifier) issued to you by the IEEE. See this link for the global registry of OUI values. This OUI value ends up in the first 3 bytes (or just the top 22 bits, really).
MAC addresses aren't that complicated, so you should probably also just have a look at the definition. Wikipedia has a good one.

Modified from mamofish's code to Python3:
mac = '00:00:00:'
for number in range(16**6):
hex_num = hex(number)[2:].zfill(6)
print("{}{}{}:{}{}:{}{}".format(mac,*hex_num))
Generates mac addresses as strings from 00:00:00:00:00:00 to 00:00:00:ff:ff:ff.

Since uniqueness is all you should care about (beyond making the address well-formed), I'd worry about the MSBs in the OUI and use a sequence in the NIC specific bytes. The distribution of the addresses is likely unimportant to your application (even though some NIC or switch implementations might use them as an input to a hash, this is likely not to be a big concern).
You may want to consider using the "locally administered" flag in the OUI to avoid a conflict with an existing device manufacturer.
Avoid pitfalls like setting the multicast bit (your example does).

To avoid duplicates:
If you're going to generate a LOT (millions) of such MAC addresses, you might want to generate an in-order list of MAC's, feed that to a linear randomization process (GNU sort -R should do fine - I don't think it does this in linear time, but it has a similar end result) once, and then pull your fresh addresses off one end of the randomized list as needed. I believe such a list should fit in about 34 megabytes.
If you merely need thousands, you're probably better off maintaining a text file with already-selected values, and checking for collisions against that, adding new values as you go. This is a slower algorithm asympotically speaking, but it has a much less overhead, so it should still be a win for lower numbers of mac addresses.
BTW, should the 4th octet (numbered from 1 starting on the left), be 0-ff instead of 0-7f? I see no occurrences of 7f or 127 in the Wikipedia page on Mac addresses:
http://en.wikipedia.org/wiki/MAC_address

Related

f-string script conversion to earlier version of Python

I have a question in regards to comparing files using python. For context, the problem I am having is that I have two firewalls with different configurations (over 14000 lines each on Notepad++) and I need to find the differences and annotate them.
Quick Example -
Firewall 1:
Version: 123
set policy EXPLICIT_DENY ALL
IP address allow 1.2.3.4
IP address allow 4.3.2.1
set policy EXPLICIT_ALLOW NONE
Firewall 2:
Version: 321
set policy EXPLICIT_ALLOW NONE
IP address allow 4.3.2.1
IP address allow 1.2.3.4
set policy EXPLICIT_DENY ALL
A line-by-line comparison would show that all of those lines are incorrect because they do not match side by side, however, the configuration is the same and would not need to be annotated. The only difference would be the Version # in the example. The script below was able to work for my purposes.
Current Script I ran -
'file1 = open("OLD FW.txt",'r')
'file2 = open("NEW FW.txt",'r')
'file3 = open("Results.txt",'r+')
'file1_lines = file1.readlines()
'file2_lines = file2.readlines()
'for position, a in enumerate(file1_lines):
'linematch = False
'for b in file2_lines:
'if a == b:
'linematch = True
'if linematch == False:
'file.3write(f"{position+1}: {a.strip()}\")
'file1.close()
'file2.close()'
The output would show every line from the OLD firewall that does not appear on the NEW firewall. This would effectively let me see what configurations are missing and/or different AND show me what line is is on the original FW.
The issue I figured out after coming up with this is that my current software version at work is only Python 2.7.16 which doesn't support f-strings. I did a little bit of research but am far to novice currently to figure this out in the short time window I have.
Mai question: How do I convert this Python f-string script to something that would work the same in an older version of Python that doesn't support f-strings?
Thanks in advance to anyone who can help me figure this out!
For simple cases like you show, you can use the .format() method.
file.write("{}: {}\n".format(position+1, a.strip()))

get unique machine id in python 2.7

import pycpuid
b=pycpuid.cpuid(1)
pid=str(b[0]+b[3])
Is that the same every time system reboots?
Not sure what passing different ints does to cpuid function?
ie: no matter what OS or version of that OS is installed, or network adapters...
if python is run and this function used, it will always return same value
thats what im looking for, based on the CPU, no other hardware
The CPUID call will not be unique (but it won't change either - so it will be the same across OSes (it's an opcode implemented in the processor)) for a specific computer. Only a subset of processors (in the Pentium 3 range for Intel and a few other brands) returns an actual serial number. AMD does not return a serial number in any version.
The integer given to cpuid tells it what information it should retrieve - see the EAX column in the documentation for the opcode - i.e. if it should return a specific check for a feature. You shouldn't have to call it directly, but instead use the helper functions defined in pycpuid (which gives arguments according to what it should retrieve).
If you're calling cpuinfo with an argument of 1, you're asking it to return the information given when EAX is 1:
INPUT EAX = 1: Returns Model, Family, Stepping Information
When CPUID executes with EAX set to 1, version information is returned in EAX.
INPUT EAX = 1: Returns Additional Information in EBX
When CPUID executes with EAX set to 1, additional information is returned to the EBX register: - Brand index (low byte of EBX) - this number provides an entry into a brand string table that contains brand strings for IA-32 processors. More information about this field is provided later in this section.
CLFLUSH instruction cache line size (second byte of EBX)
this number indicates the size of the cache line flushed with CLFLUSH instruction in 8-byte increments. This field was introduced in the Pentium 4 processor.
Local APIC ID (high byte of EBX)
this number is the 8-bit ID that is assigned to the local APIC on the processor during power up. This field was introduced in the Pentium 4 processor.
Latest Linux versions come with auto-generated machine id which can be found at /etc/machine-id .
import os
machineId = os.popen("cat /etc/machine-id").read()
print(machineId)
Ubuntu Documentation for Machine Id
FreeDesktop Documentation for Machine Id
I would recommend using the following code and syntax instead of pycpuid. I hope this helps.
#The following can be used to generate a random id
import uuid
# Printing random id using uuid1()
print ("The random id using uuid1() is : ",end="")
print (uuid.uuid1())
#The following can be used to retrieve the MAC ADDRESS which is necessary for #the machine's specific ID
from uuid import getnode as get_mac
mac = get_mac()
#Prevents spoofing of MAC ADDRESS
>>> print uuid.getnode.__doc__
Get the hardware address as a 48-bit positive integer.
The first time this runs, it may launch a separate program, which could
be quite slow. If all attempts to obtain the hardware address fail, we
choose a random 48-bit number with its eighth bit set to 1 as recommended
in RFC 4122.
On linux machines, the convention is to read /etc/machine-id. Which is a randomly generated id.

Passing a record over a socket

I have basic socket communication set up between python and Delphi code (text only). Now I would like to send/receive a record of data on both sides. I have a Record "C compatible" and would like to pass records back and forth have it in a usable format in python.
I use conn.send("text") in python to send the text but how do I send/receive a buffer with python and access the record items sent in python?
Record
TPacketData = record
pID : Integer;
dataType : Integer;
size : Integer;
value : Double;
end;
I don't know much about python, but I have done a lot between Delphi, C++, C# and Java even with COBOL.Anyway, to send a record from Delphi to C first you need to pack the record at both ends,
in Deplhi
MyRecord = pack record
in C++
#pragma pack(1)
I don’t know in python but I guess there must be a similar one. Make sure that at both sides the sizeof(MyRecord) is the same length.Also, before sending the records, you should take care about byte ordering (you know, Little-Endian vs Big-Endian), use the Socket.htonl() and Socket.ntohl() in python and the equivalent in Deplhi which are in WinSock unit. Also a "double" in Delphi could not be the same as in python, in Delphi is 8 bytes check this as well, and change it to Single(4 bytes) or Extended (10 bytes) whichever matches.
If all that match then you could send/receive binary records in one shut, otherwise, I'm afraid, you have to send the individual fields one by one.
I know this answer is a bit late to the game, but may at least prove useful to other people finding this question in their search-results. Because you say the Delphi code sends and receives "C compatible data" it seems that for the sake of the answer about Python's handling it is irrelevant whether it is Delphi (or any other language) on the other end...
The python struct and socket modules have all the functionality for the basic usage you describe. To send the example record you would do something like the below. For simplicity and sanity I have presumed signed integers and doubles, and packed the data in "network order" (bigendian). This can easily be a one-liner but I have split it up for verbosity and reusability's sake:
import struct
t_packet_struc = '>iiid'
t_packet_data = struct.pack(t_packet_struc, pid, data_type, size, value)
mysocket.sendall(t_packet_data)
Of course the mentioned "presumptions" don't need to be made, given tweaks to the format string, data preparation, etc. See the struct inline help for a description of the possible format strings - which can even process things like Pascal-strings... By the way, the socket module allows packing and unpacking a couple of network-specific things which struct doesn't, like IP-address strings (to their bigendian int-blob form), and allows explicit functions for converting data bigendian-to-native and vice-versa. For completeness, here is how to unpack the data packed above, on the Python end:
t_packet_size = struct.calcsize(t_packet_struc)
t_packet_data = mysocket.recv(t_packet_size)
(pid, data_type, size, value) = struct.unpack(t_packet_struc,
t_packet_data)
I know this works in Python version 2.x, and suspect it should work without changes in Python version 3.x too. Beware of one big gotcha (because it is easy to not think about, and hard to troubleshoot after the fact): Aside from different endianness, you can also distinguish between packing things using "standard size and alignment" (portably) or using "native size and alignment" (much faster) depending on how you prefix - or don't prefix - your format string. These can often yield wildly different results than you intended, without giving you a clue as to why... (there be dragons).

CIDR subnet calculation and python ipcalc

I am questioning the results of the ipcalc module (ipcalc) for Python (it seems that netaddr may be a better choice).
Let's take 192.168.1.25/30 as an example. In binary, the last octet is 00011001 AND 11111100 = 00011000, so I get 192.168.1.24 as the Network ID and the range 192.168.1.24 - 192.168.1.27.
Using ipcalc, when I specify
subnet = ipcalc.Network('192.168.1.25/30')
for x in subnet: print x
The output is
192.168.1.25
192.168.1.26
192.168.1.27
192.168.1.28
I am not understanding the inconsistency. When using CIDR notation, it seems that specifying both 192.168.1.24/30 and 192.168.1.25/30 (or .26/30 or .27/30) refer to the same subnet.
Is that correct? Is this just a bug in the ipcalc module?
There is an open bug for this at the moment: No way to resolve IP + Netmask to Network Object
And an earlier bug report that discuss the matter: Strange subnet calculations
But they have also added a function called network to get the network address from an IP. From the manual:
>>> localnet = Network('127.128.99.3/8')
>>> print localnet.network()
127.0.0.0
The manual specifically says that the constructor Network should take a network address as its first argument, not any IP in the network. Rather confusing if you ask me (especially since the above code block breaks that condition). I would at least read the code for the module before using it.
It's correct the subnet is 192.168.1.24/30 so the ips 192.168.1.24 to 192.168.1.27 make part of this network.

TCP Sockets: Double messages

I'm having a problem with sockets in python.
I have a a TCP server and client that send each other data in a while 1 loop.
It packages up 2 shorts in the struct module (struct.pack("hh", mousex, mousey)). But sometimes when recving the data on the other computer, it seems like 2 messages have been glued together. Is this nagle's algorithm?
What exactly is going on here? Thanks in advance.
I agree with other posters, that "TCP just does that". TCP guarantees that your bytes arrive in the right order, but makes no guarantees about the sizes of the chunks they arrive in. I would add that TCP is also allowed to split a single send into multiple recv's, or even for example to split aabb, ccdd into aab, bcc, dd.
I put together this module for dealing with the relevant issues in python:
http://stromberg.dnsalias.org/~strombrg/bufsock.html
It's under an opensource license and is owned by UCI. It's been tested on CPython 2.x, CPython 3.x, Pypy and Jython.
HTH
To be sure I'd have to see actual code, but it sounds like you are expecting a send of n bytes to show up on the receiver as exactly n bytes all the time, every time.
TCP streams don't work that way. It's a "streaming" protocol, as opposed to a "datagram" (record-oriented) one like UDP or STCP or RDS.
For fixed-data-size protocols (or any where the next chunk size is predictable in advance), you can build your own "datagram-like receiver" on a stream socket by simply recv()ing in a loop until you get exactly n bytes:
def recv_n_bytes(socket, n):
"attempt to receive exactly n bytes; return what we got"
data = []
while True:
have = sum(len(x) for x in data)
if have >= n:
break
want = n - have
got = socket.recv(want)
if got == '':
break
return ''.join(data)
(untested; python 2.x code; not necessarily efficient; etc).
You may not assume that data will become available for reading from the local socket in the same size pieces it was provided for sending at the other source end. As you have seen, this might sometimes be usually true, but by no means reliably so. Rather, what TCP guarantees is that what goes in one end will eventually come out the other, in order without anything missing or if that cannot be achieved by means built into the protocol such as retries, then whole thing will break with an error.
Nagle is one possible cause, but not the only one.

Categories