Search code examples
pythonsocketsmulticastpim

python socket programming: Crafting pim-join-prune packet


Good afternoon everyone,

my coding skills are not quite on the level required to do socket programming myself and I was wondering if someone here can help me with what I need - which is fairly simple:

  • crafting own PIM (multicast) join packet.

I want to send a "dummy" PIM join packets to a neighbouring device without really bothering maintaining any PIM adjacencies/(hellos). Ideally I want to end-up with a function that accepts a few arguments:

def send_dummy_join(group, rp, source_ip):
  // send 1 join every time this function is called

Ideally I need it to run using python3 not using external modules (e.g. scapy)

Another problem I have & don't quite understand how to solve is crafting a packet with a source address which doesn't belong to any network interface on the system. I know scapy can do this I only don't quite understand how to craft it myself.

Thanks in advance for all your help. Much appreciated!

Some external information.

PIM headers

http://www.networksorcery.com/enp/protocol/pim.htm

PIM.c implementation

https://github.com/troglobit/pimd/blob/master/src/pim.c


Solution

  • I will most probably (fully rightfully though ;-)) get publicly lynched for the following piece of code, but as one network engineer to another check out the code below.

    A few more bits to bear in mind - You need to "establish adjacency" first (exchange some hello packets) or your PIM joins will get ignored by the upstream router (hence the dummy hello function added).

    Furthermore, the "Upstream Neighbour IP" is also necessary when sending PIM joins.

    import socket
    from struct import pack, iter_unpack
    from functools import reduce    
    
    def checksum(data):
        sum = reduce(lambda x, y: x + y, iter_unpack("!H",data))
        s = reduce(lambda x, y: x + y, sum)
    
        while (s >> 16):
            s = (s & 0xFFFF) + (s >> 16)
        s = ~s & 0xffff
    
        return s
    
    def send_dummy_hello():
        PIM_GROUP = '224.0.0.13'
    
        sock = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_PIM)
        my_packet=pack('!BBH3H4H4H4H', 32, 0, 24707, 1, 2, 105, 20, 4, 16121,
            16078, 19, 4, 0, 1, 21, 4, 256, 0)
    
        sock.sendto(my_packet, (PIM_GROUP,0))
    
    def send_dummy_join(group, rp_addr, neighbor_ip):
        PIM_GROUP = '224.0.0.13'
    
        sock = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_PIM)
    
        pim_ver    = 35
        pim_res    = 0
        pktchksum  = 0
        pim_opts   = 256
        neighbor   = socket.inet_aton(neighbor_ip)
        ngroups    = 1
        holdtime   = 210
        subnet     = socket.inet_aton('1.0.0.32')
        group_a    = socket.inet_aton(group)
        num_joins  = 1
        num_prunes = 0
        rrp_subnet = socket.inet_aton('1.0.7.32')
        rp_address = socket.inet_aton(rp_addr)
    
        my_packet = pack('!BB2H4sHH4s4sHH4s4s', pim_ver, pim_res, pktchksum, pim_opts,
            neighbor, ngroups, holdtime, subnet, group_a, num_joins, num_prunes, 
            rrp_subnet, rp_address)
    
        pktchksum = checksum(my_packet)
        my_packet = pack('!BB2H4sHH4s4sHH4s4s', pim_ver, pim_res, pktchksum, pim_opts,
            neighbor, ngroups, holdtime, subnet, group_a, num_joins, num_prunes, 
            rrp_subnet, rp_address)
    
        sock.sendto(my_packet, (PIM_GROUP,0))
        print (f'PIM Join sent to {neighbor_ip} for {group} & RP {rp_addr}')
    
    >>>send_dummy_hello() 
    >>>send_dummy_join('239.10.20.30','192.168.0.1','10.0.0.102')
    
    
    

    I tested this with EVE-NG and it (magically!) worked :-)

    EDIT:

    The code is now edited to support header checksum calculation. I think this is now complete! :-)