/*      $NetBSD: pf-linux2.c,v 1.2 2020/10/27 17:07:14 abs Exp $        */

/*
* General Purpose AppleTalk Packet Filter Interface
*
* Copyright (c) 1992-1995, The University of Melbourne.
* All Rights Reserved.  Permission to redistribute or
* use any part of this software for any purpose must
* be obtained in writing from the copyright owner.
*
* This software is supplied "as is" without express
* or implied warranty.
*
* [email protected]
*
* Supports:
*      Linux SOCK_PACKET
*
*
* Modified for use with the linux-mopd port by Karl Maftoum
* [email protected]
*
*/

#include "port.h"
__RCSID("$NetBSD: pf-linux2.c,v 1.2 2020/10/27 17:07:14 abs Exp $");

/*
* include header files
*
*/

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <sys/file.h>
#include <sys/socket.h>
#include <net/if.h>
#include <sys/errno.h>
#include <linux/if_ether.h>
#include <netdb.h>
#include <ctype.h>
#include <string.h>

#define MOPDEF_SUPRESS_EXTERN
#include "mopdef.h"

/*
* definitions
*
*/

#define READBUFSIZ      4096
#define NUMRDS          32

struct RDS {
 u_short dataLen;
 u_char *dataPtr;
};

/*
* variables
*
*/

struct socklist {
 int iflen;
 struct sockaddr sa;
} socklist[32];

struct ifreq ifr;
extern int errno;
extern int promisc;

struct RDS RDS[NUMRDS];
static int setup_pf(int, int, u_short);

/*
* Open and initialize packet filter
* for a particular protocol type.
*
*/


int
pfInit(char *interface, int mode, u_short protocol, int typ)
{
 int s;
 int ioarg;
 char device[64];
 unsigned long if_flags;


 { u_short prot;

   prot = ((typ == TRANS_8023) ? htons(ETH_P_802_2) : htons(protocol));
   if ((s = socket(AF_INET, SOCK_PACKET, prot)) < 0) {
     perror(interface);
     return(-1);
   }
   if (s >= 32) {
     close(s);
     return(-1);
   }
 }

 /*
  * set filter for protocol and type (IPTalk, Phase 1/2)
  *
  */

 if (setup_pf(s, protocol, typ) < 0)
   return(-1);

 /*
  * set options, bind to underlying interface
  *
  */

 strncpy(ifr.ifr_name, interface, sizeof(ifr.ifr_name));

 /* record socket interface name and length */
 strncpy(socklist[s].sa.sa_data, interface, sizeof(socklist[s].sa.sa_data));
 socklist[s].iflen = strlen(interface);

 return(s);
}

/*
* establish protocol filter
*
*/

static int
setup_pf(int s, int typ, u_short prot)
{
 int ioarg;
 u_short offset;
 return(0);
}

/*
* get the interface ethernet address
*
*/

int
pfEthAddr(int s, char *interface, u_char *addr)
{
 strncpy(ifr.ifr_name, interface, sizeof (ifr.ifr_name) -1);
 ifr.ifr_name[sizeof(ifr.ifr_name)] = 0;
 ifr.ifr_addr.sa_family = AF_INET;
 if (ioctl(s, SIOCGIFHWADDR, &ifr) < 0) {
   perror("SIOCGIFHWADDR");
   return(-1);
 }
 memcpy((char *)addr, ifr.ifr_hwaddr.sa_data, 6);
 return(0);
}

/*
* add a multicast address to the interface
*
*/

int
pfAddMulti(int s, char *interface, u_char *addr)
{
 int sock;

 strncpy(ifr.ifr_name, interface, sizeof (ifr.ifr_name) - 1);
 ifr.ifr_name[sizeof(ifr.ifr_name)] = 0;

 ifr.ifr_addr.sa_family = AF_UNSPEC;
 bcopy((char *)addr, ifr.ifr_addr.sa_data, 6);

 /*
  * open a socket, temporarily, to use for SIOC* ioctls
  *
  */
 if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
   perror("socket()");
   return(-1);
 }
 if (ioctl(sock, SIOCADDMULTI, (caddr_t)&ifr) < 0) {
   perror("SIOCADDMULTI");
   close(sock);
   return(-1);
 }
 close(sock);

 return(0);
}

/*
* delete a multicast address from the interface
*
*/

int
pfDelMulti(int s, char *interface, u_char *addr)
{
 int sock;

 strncpy(ifr.ifr_name, interface, sizeof (ifr.ifr_name) - 1);
 ifr.ifr_name[sizeof(ifr.ifr_name)] = 0;

 ifr.ifr_addr.sa_family = AF_UNSPEC;
 bcopy((char *)addr, ifr.ifr_addr.sa_data, 6);

 /*
  * open a socket, temporarily, to use for SIOC* ioctls
  *
  */
 if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
   perror("socket()");
   return(-1);
 }
 if (ioctl(sock, SIOCDELMULTI, (caddr_t)&ifr) < 0) {
   perror("SIOCDELMULTI");
   close(sock);
   return(-1);
 }
 close(sock);

 return(0);
}

/*
* return 1 if ethernet interface capable of multiple opens
*
*/

int
eth_mopen(int phase)
{
 if (phase == 2)
   return(0);
 return(1);
}

/*
* read a packet
* Read Data Structure describes packet(s) received
*
*/




int
pfRead(int fd, u_char *buf, int len)
{
 int i, cc;

 int fromlen;
 struct sockaddr sa;

 RDS[0].dataLen = 0;
 fromlen = sizeof(struct sockaddr);

 if ((cc = recvfrom(fd, (char *)buf, len, 0, &sa, &fromlen)) <= 0)
   return(cc);

 /* check if from right interface */
 for (i = socklist[fd].iflen-1; i >= 0; i--)
   if (sa.sa_data[i] != socklist[fd].sa.sa_data[i])
     return(0);

 RDS[0].dataLen = cc;
 RDS[0].dataPtr = buf;
 RDS[1].dataLen = 0;

 return(cc);
}

/*
* write a packet
*
*/

int
pfWrite(int fd, u_char *buf, int len)
{

 if (sendto(fd, buf, len, 0, &socklist[fd].sa, sizeof(struct sockaddr)) == len)
   return(len);

 return(-1);
}

/*
* Return information to device.c how to open device.
* In this case the driver can handle both Ethernet type II and
* IEEE 802.3 frames (SNAP) in a single pfOpen.
*/

int
pfTrans(char *interface)
{
       return TRANS_ETHER+TRANS_8023;
}