The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.
/*
Perl ARP Extension
Create and send an arp packets, lookup mac addresses

Programmed by Bastian Ballmann
Last update: 22.06.2013

This program is free software; you can redistribute 
it and/or modify it under the terms of the 
GNU General Public License version 2 as published 
by the Free Software Foundation.

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. 
*/

#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"

#include "ppport.h"
#include <stdio.h>           
#include <stdlib.h>          
#include <string.h>          
#include <errno.h>           
#include <net/ethernet.h>    
#include <netinet/ether.h>
#include <net/if.h>
#include <arpa/inet.h>       
#include "arp.h"

MODULE = Net::ARP		PACKAGE = Net::ARP		

int
send_packet(dev, sip, dip, smac, dmac, type)
	const char *dev;
	const char *sip;
	const char *dip;
	const char *smac;
	const char *dmac;
	const char *type;

	CODE:
	  int uid;
	  unsigned int packetsize = sizeof(struct my_arphdr) + sizeof(struct ether_header);
	  unsigned char packet[packetsize];
	  struct ether_header *ethhdr = (struct ether_header *)packet;
	  struct my_arphdr *arp = (struct my_arphdr *)(packet + sizeof(struct ether_header));
	  u_short op;	
	  in_addr_t ipaddr;

          RETVAL = 1;

	  // Are you root?
	  uid = getuid();
	  if(uid != 0) 
	  { 
	    printf("You must have UID 0 instead of %d.\n",uid); 
	    exit(0);
	  }

	  // Initialize packet buffer 
	  memset(packet,0,packetsize);

	  // What's the ARP operation type?
	  if(!strcmp(type,"request"))
	  {
	    op = ARPOP_REQUEST;
	  }
	  else if(!strcmp(type,"reply"))
	  {
	    op = ARPOP_REPLY;
	  }
	  else if(!strcmp(type,"revrequest"))
	  {
	    op = ARPOP_REVREQUEST;
	  }
	  else if(!strcmp(type,"revreply"))
	  {
	    op = ARPOP_REVREPLY;
	  }
	  else if(!strcmp(type,"invrequest"))
	  {
	    op = ARPOP_INVREQUEST;
	  }
	  else if(!strcmp(type,"invreply"))
	  {
	    op = ARPOP_INVREPLY;
	  }
	  else
	  {
	    printf("Unknown ARP operation\n");
	    RETVAL = 0;
	  }

	  if(smac == NULL)
	  {
	    printf("Parameter smac is NULL! Terminating.\n");
	    RETVAL = 0;
	  }

	  if(dmac == NULL)
	  {
	    printf("Parameter dmac is NULL! Terminating.\n");
	    RETVAL = 0;
	  }

          // Found a dollar sign?
	  if(strchr(smac,36))
	  {
	    printf("Found a $ char in smac! Terminating.\n");
	    RETVAL = 0;
	  }

	  if(strchr(dmac,36))
	  {
	    printf("Found a $ char in dmac! Terminating.\n");
	    RETVAL = 0;
	  }

          if(ether_aton(smac) == NULL)
          {
            printf("Invalid source mac address! Terminating.\n");
            RETVAL = 0;
          }

          if(ether_aton(dmac) == NULL)
          {
            printf("Invalid destination mac address! Terminating.\n");
            RETVAL = 0;
          }

          // Check ips
          if(inet_addr(sip) == INADDR_NONE)
          {
            printf("Invalid source ip address! Terminating.\n");
            RETVAL = 0;
          }

          if(inet_addr(dip) == INADDR_NONE)
          {
            printf("Invalid destination ip address! Terminating.\n");
            RETVAL = 0;
          }

          // Construct and send packet
          if(RETVAL != 0)
          {
	  	// Ethernet header
	        memcpy(ethhdr->ether_dhost,(u_char *)ether_aton(dmac),ETHER_ADDR_LEN); // Destination MAC
	        memcpy(ethhdr->ether_shost,(u_char *)ether_aton(smac),ETHER_ADDR_LEN); // Source MAC
	        ethhdr->ether_type = htons(ETHERTYPE_ARP);                             // ARP protocol

	        // ARP header
	        arp->hw_type = htons(ARPHDR_ETHER);                                    // Hardware address type
	        arp->proto_type = htons(ETH_P_IP);                                     // Protocol address type
	        arp->ha_len = ETH_ALEN;                                                // Hardware address length
	        arp->pa_len = IP_ALEN;                                                 // Protocol address length
	        arp->opcode = htons(op);                                               // ARP operation
	        memcpy(arp->source_add,(u_char *)ether_aton(smac),ETH_ALEN);           // Source MAC
		memcpy(arp->dest_add,(u_char *)ether_aton(dmac),ETH_ALEN);             // Destination MAC
		ipaddr = inet_addr(sip);
		memcpy(arp->source_ip, (u_char *)&ipaddr, IP_ALEN);		       // Source IP

		ipaddr = inet_addr(dip);
		memcpy(arp->dest_ip, (u_char *)&ipaddr, IP_ALEN);		       // Destination IP



	         // Run packet!! Run!
	         // FreeBSD code
	         if(SOCK_TYPE == SOCK_RAW)
	         {
	           RETVAL = send_packet_bsd(dev,packet,packetsize);	
	         }

	         // Linux code
	         else
	         {
	           RETVAL = send_packet_linux(dev,packet,packetsize);
	         }
          }

          OUTPUT:
          RETVAL

char *
get_mac(dev)
	const char *dev;
	CODE:
          char tmp[HEX_HW_ADDR_LEN] = "unknown";

	  if(SOCK_TYPE == SOCK_RAW)
	  {
	    get_mac_bsd(dev,tmp);
	  }
	  else
	  {
	    get_mac_linux(dev,tmp);
	  }

	  RETVAL = tmp;

	OUTPUT:
	RETVAL


char *
arp_lookup(dev, ip)
	const char *dev;
	const char *ip;

	CODE:
	  char tmp[HEX_HW_ADDR_LEN] = "unknown";

	  if(SOCK_TYPE == SOCK_RAW)
	  {
	    arp_lookup_bsd(dev,ip,tmp);
	  }
	  else
	  {
	    arp_lookup_linux(dev,ip,tmp);
	  }

	  RETVAL = tmp;

          OUTPUT:
          RETVAL