How to use tun device in Windows C program - c

I have used tun devices on Linux before and need to port my code on Windows. I have installed tun/tap driver from OpenVPN on Windows, but I am not able to figure out how to create a device handle for that tun device in Windows C program.
Basically I need help creating the tun device and getting the handle.
The code I use on Linux is as follows (similar to the example on kernel.org)
int mktun (char * dev, int flags, struct ifreq * ifr) {
int fd, stat;
char * clonedev = "/dev/net/tun";
/* Get the file descriptor from the tun clone to use as input to ioctl() */
if ( (fd = open(clonedev, O_RDWR) ) < 0 )
return fd;
/* Now prepare the structure ifreq for ioctl() */
memset(ifr, 0, sizeof(*ifr)); /* reset memory to 0 */
ifr->ifr_flags = flags; /* set the flags IFF_TUN or IFF_TAP and IFF_NO_PI */
if (*dev)
strcpy(ifr->ifr_name, dev);
/* Now we try and create a device */
if ( (stat = ioctl(fd, TUNSETIFF, (void *) ifr) ) < 0 ) {
perror("ioctl()");
close(fd);
return stat;
}
/* Now write back the name of the interface to dev just to be sure */
strcpy(dev, ifr->ifr_name);
/* Now return the file descriptor that can be used to talk to the tun interface */
return fd;
}

Related

use of promiscuous mode

I have connected 3 laptops in same LAN.
lap-1: 192.168.1.2
lap-2: 192.168.1.3
lap-3: 192.168.1.4
I made lap-1 as server and listen on 9333 port. lap-2 acts as client. Using netcat I sent data from lap2 to lap1. I'm able to capture packets using pcap in lap1. I have turned on promiscuous mode using sudo ifconfig eth0 promisc. Also in pcap_live_open method I have set promiscuous mode flag.
Then I turned off promiscuous mode and also in pcap_live_open function. Still I'm able to capture packets.
I googled about promiscuous mode and what I could infer was if device opens an interface in promiscuous mode it would able to capture all packets attached to that network.
so considering this, I made acting lap-3 as server and lap-2 remains as client. I followed the same procedure as above. I run the pcap executable in lap-1 hoping that I would able to capture packets transferred between lap-3 and lap-2 but pcap running in lap-1 is not able to do so with promiscuous mode on. All 3 laps are connected to same network.
Can anyone enlighten me the use of promiscuous mode with simple scenario?
This is my pcap code:
29988 is reverse(swap) of 9333, I'm just looking for that.
#include <pcap/pcap.h>
#include <stdint.h>
const u_char *packet;
int main()
{
char *dev = "eth0";
pcap_t *handle;
int j=0;
char errbuf[PCAP_ERRBUF_SIZE];
struct bpf_program fp;
bpf_u_int32 mask;
bpf_u_int32 net;
struct pcap_pkthdr header;
uint8_t *ip_header_len;
uint16_t ip_header_len_val;
uint16_t *port;
/* Find the properties for the device */
while (pcap_lookupnet(dev, &net, &mask, errbuf) == -1) {
printf("Couldn't get netmask for device %s: %s\n", dev, errbuf);
net = 0;
mask = 0;
}
printf("lookedup pcap device: %s\n", dev);
/* Open the session in promiscuous mode */
handle = pcap_open_live(dev, BUFSIZ,1,0, errbuf);
if (handle == NULL) {
printf("Couldn't open device %s: %s\n", dev, errbuf);
}
/* Compile and apply the filter */
if (pcap_compile(handle, &fp, filter_exp, 0, net) == -1) {
printf("Couldn't parse filter %s: %s\n", filter_exp, pcap_geterr(handle));
pcap_close(handle);
}
/* if (pcap_setfilter(handle, &fp) == -1) {
printf("Couldn't install filter %s: %s", filter_exp, pcap_geterr(handle));
return(-1);
}
*/
/* Grab a packet */
while ((packet = pcap_next(handle, &header)) != NULL)
{
uint16_t *data_size;
uint16_t size,total_len_val,tcp_header_len_val;
char tdata[128];
uint8_t *data,*tcp_header_len;
uint16_t *total_len;
//ip_proto = (uint8_t *)&packet[9];
ip_header_len = (uint8_t *)&packet[14];
ip_header_len_val = (*ip_header_len) & 0x0F;
ip_header_len_val = ip_header_len_val*4;
// printf("IP header len val:%d\n",ip_header_len_val);
port = (uint16_t *)&packet[14+ip_header_len_val+2];
//printf("port:%d\n",*port);
total_len = (uint16_t *)&packet[14+2];
total_len_val = ((*total_len) >> 8) & 0x00FF;
total_len_val = total_len_val + (((*total_len) << 8) & 0xFF00);
//total_len_val=*total_len;
// printf("tot len val:%d\n",total_len_val);
tcp_header_len = (uint8_t *)&packet[14+ip_header_len_val+12];
tcp_header_len_val = (*tcp_header_len) & 0xF0;
tcp_header_len_val = tcp_header_len_val>>4;
tcp_header_len_val = tcp_header_len_val * 4;
// printf("tcp header len val:%d\n",tcp_header_len_val);
size = (total_len_val- ip_header_len_val) - tcp_header_len_val;
data = (uint8_t *)&packet[14+ip_header_len_val+tcp_header_len_val];
memset(tdata,0,128);
mempcpy(tdata,data,size);
tdata[size]='\0';
if((*port)==29988)
{
printf("Data Packet:%s\n",tdata);
}
}
}
I expect that when you say that they are all on the same network, that what you mean is that they are connected to the same Ethernet switch. That switch will only send data to laptop1 that is destined for laptop1. In the old days when it was common to use an Ethernet hub, then all traffic went to all connected devices, but now a switch is very cheap and hubs are no longer common. If you can find a hub, then you can try this out, but otherwise you will only ever be able to see traffic destined for your device.
As Brad mentioned, the router knows at which port the destined device is connected, so it only send the packets there. If you want to try this out, you can use VirtualBox or VMware, and connect the machines in a virtual network.

Knowing which file-descriptors are ready after select() call in C

I'm new to socket programming and I've been introduced to the select() system call. My question is, lets say I'm writing a server in C (which I am attempting to do) and I want to use the select() call in my implementation for practice. I'm trying to write a server that receives information from a client, so my approach is to use select(), followed by read() and just output the information.
According to the documentation I've read select() returns the number of file descriptors in the input set which are ready for i/o. My question is, how do know which file descriptors in the original set are the ones that are ready for i/o? I can't seem to find this in my searches or examples I've looked at for the past while.
Let's say my code looks like the below:
int main() {
/* Create socket/server variables */
int select_value;
int this_socket;
int maxfd;
struct sockadder_in address;
fd_set allset;
/* Bind the socket to a port */
main_socket = socket(AF_INET, SOCK_STREAM, 0);
if (main_socket < 0) {
perror("socket()");
exit(1);
}
Connect(main_socket, (struct sockaddr *)&address, sizeof(address));
/* Add the socket to the list of fds to be monitored */
FD_ZERO(&allset);
FD_SET(main_socket, &allset);
fd_set read_ready = allset;
fd_set write_ready = allset;
while (1) {
/* Listen for a connection */
/* Accept a connection */
select_value = Select(maxfd+1, &read_ready, &write_ready, NULL, NULL);
if (select_value == -1) {
perror("select()");
exit(1);
}
else if(select_value > 0) {
/* How to access i/o ready file descriptors
now that we know there are some available? */
}
}
}
One can do this using the FD_ISSET macro that is part of <sys/select.h>.
When your select unblocks and a file descriptor is ready, you can test all of your file descriptors using the FD_ISSET macro in a simple loop. This can be translated to the following sample :
for (i = 0; i < FD_SETSIZE; ++i) {
if (FD_ISSET (i, &read_fd_set)) {
if (i == bound_socket) {
// A new client is waiting to be accepted
new = accept(sock, (struct sockaddr *) &clientname, &size);
// ...
FD_SET (new, &active_fd_set);
}
else {
// There is something to be read on the file descriptor.
data = read_from_client_on(i);
}
}
}
Of course, this is just sample which is obviously lacking any error handling, which you should handle in your application.

Unable to claim USB interface with C + libusb on Mac OS X

I've got a compound USB + CDC device I built using a PIC32 microcontroller, and I'm trying to connect to the device and send some data to the CDC data interface endpoint from my Mac.
I know the circuit works 100%, as the device registers as both a HID joystick, and I'm able to connect to the device using Zoc terminal, on /dev/tty.usbmodemfa132. I can send commands with Zoc, and see my MCU responding to these commands by blinking some LEDs on the circuit.
I'm running this on Mac OS X Mavericks, but had the same problem with a similar example I gave up on, a few weeks ago on Mountain Lion.
My code looks like follows:
// Includes -----------------------------------------------------------------------------------------------------------
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <libusb-1.0/libusb.h>
#include <unistd.h>
// Defines ------------------------------------------------------------------------------------------------------------
#define VID 0x04d8
#define PID 0x005e
#define CDC_DATA_INTERFACE_ID 2
// Function Declarations ----------------------------------------------------------------------------------------------
void print_device(libusb_device *device);
void send(libusb_context *usb_context, uint16_t vid, uint16_t pid);
// Function Definitions -----------------------------------------------------------------------------------------------
/**
* main
*/
int main(int argc, char **argv)
{
libusb_device **usb_devices = NULL;
libusb_context *usb_context = NULL;
ssize_t device_count = 0;
bool debug_enabled = false;
int c;
// Collect command line attributes
while ( (c = getopt(argc, argv, "d")) != -1) {
switch (c) {
case 'd':
debug_enabled = true;
break;
}
}
// Initialize USB context
int result = libusb_init(&usb_context);
if(result < 0) {
printf("Unable to initialise libusb!");
return EXIT_FAILURE;
}
// Turn debug mode on/off
if(debug_enabled) {
libusb_set_debug(usb_context, 3);
}
// Get USB device list
device_count = libusb_get_device_list(usb_context, &usb_devices);
if(device_count < 0) {
puts("Unable to retrieve USB device list!");
}
// Iterate and print devices
puts("VID PID Manufacturer Name\n------ ------ -------------------");
for (int i = 0; i < device_count; i++) {
print_device(usb_devices[i]);
}
// Attempt to send data
send(usb_context, VID, PID);
// Cleanup and exit
libusb_free_device_list(usb_devices, 1);
libusb_exit(usb_context);
return EXIT_SUCCESS;
}
/**
* print_device
*/
void print_device(libusb_device *device)
{
struct libusb_device_descriptor device_descriptor;
struct libusb_device_handle *device_handle = NULL;
// Get USB device descriptor
int result = libusb_get_device_descriptor(device, &device_descriptor);
if (result < 0) {
printf("Failed to get device descriptor!");
}
// Only print our devices
if(VID == device_descriptor.idVendor && PID == device_descriptor.idProduct) {
// Print VID & PID
printf("0x%04x 0x%04x", device_descriptor.idVendor, device_descriptor.idProduct);
} else {
return;
}
// Attempt to open the device
int open_result = libusb_open(device, &device_handle);
if (open_result < 0) {
libusb_close(device_handle);
return;
}
// Print the device manufacturer string
char manufacturer[256] = " ";
if (device_descriptor.iManufacturer) {
libusb_get_string_descriptor_ascii(device_handle, device_descriptor.iManufacturer,
(unsigned char *)manufacturer, sizeof(manufacturer));
printf(" %s", manufacturer);
}
puts("");
libusb_close(device_handle);
}
/**
* send
*/
void send(libusb_context *usb_context, uint16_t vid, uint16_t pid)
{
libusb_device_handle *device_handle;
device_handle = libusb_open_device_with_vid_pid(usb_context, vid, pid);
if (device_handle == NULL) {
puts("Unable to open device by VID & PID!");
return;
}
puts("Device successfully opened");
unsigned char *data = (unsigned char *)"test";
if (libusb_kernel_driver_active(device_handle, CDC_DATA_INTERFACE_ID)) {
puts("Kernel driver active");
if (libusb_detach_kernel_driver(device_handle, CDC_DATA_INTERFACE_ID)) {
puts("Kernel driver detached");
}
} else {
puts("Kernel driver doesn't appear to be active");
}
int result = libusb_claim_interface(device_handle, CDC_DATA_INTERFACE_ID);
if (result < 0) {
puts("Unable to claim interface!");
libusb_close(device_handle);
return;
}
puts("Interface claimed");
int written = 0;
result = libusb_bulk_transfer(device_handle, (3 | LIBUSB_ENDPOINT_OUT), data, 4, &written, 0);
if (result == 0 && written == 4) {
puts("Send success");
} else {
puts("Send failed!");
}
result = libusb_release_interface(device_handle, CDC_DATA_INTERFACE_ID);
if (result != 0) {
puts("Unable to release interface!");
}
libusb_close(device_handle);
}
I'm getting the following error output:
libusb: 0.828223 error [darwin_open] USBDeviceOpen: another process has device opened for exclusive access
libusb: 0.828241 info [darwin_open] device open for access
Device successfully opened
Kernel driver doesn't appear to be active
libusb: 0.828641 error [darwin_claim_interface] USBInterfaceOpen: another process has device opened for exclusive access
Unable to claim interface!
libusb: 0.828766 info [event_thread_main] thread exiting
Is there a way I can release the USB device from the other process, freeing it up so I can claim it?
Is there an alternative way I can connect to /dev/tty.usbmodemfa132 to send and receive data to the CDC interface on the USB device?
An alternative to libusb perhaps?
That's right. While libusb seems to be all-powerful in Linux, you cannot use it to connect to a USB CDC interface on Mac OS X because that interface is already claimed by the AppleUSBCDCACM driver.
What you should do is use the standard way that people connect to serial ports. This will be easier because you don't have to worry about endpoints and bulk transfers and such. Here is some example cross-platform C code I wrote for one of our CDC-based products that connects to a COM port to read and write some data (source). It uses the standard functions open, read, and write.
// Uses POSIX functions to send and receive data from a Maestro.
// NOTE: You must change the 'const char * device' line below.
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#ifdef _WIN32
#define O_NOCTTY 0
#else
#include <termios.h>
#endif
// Gets the position of a Maestro channel.
// See the "Serial Servo Commands" section of the user's guide.
int maestroGetPosition(int fd, unsigned char channel)
{
unsigned char command[] = {0x90, channel};
if(write(fd, command, sizeof(command)) == -1)
{
perror("error writing");
return -1;
}
unsigned char response[2];
if(read(fd,response,2) != 2)
{
perror("error reading");
return -1;
}
return response[0] + 256*response[1];
}
// Sets the target of a Maestro channel.
// See the "Serial Servo Commands" section of the user's guide.
// The units of 'target' are quarter-microseconds.
int maestroSetTarget(int fd, unsigned char channel, unsigned short target)
{
unsigned char command[] = {0x84, channel, target & 0x7F, target >> 7 & 0x7F};
if (write(fd, command, sizeof(command)) == -1)
{
perror("error writing");
return -1;
}
return 0;
}
int main()
{
// Open the Maestro's virtual COM port.
const char * device = "\\\\.\\USBSER000"; // Windows, "\\\\.\\COM6" also works
//const char * device = "/dev/ttyACM0"; // Linux
//const char * device = "/dev/cu.usbmodem00034567"; // Mac OS X
int fd = open(device, O_RDWR | O_NOCTTY);
if (fd == -1)
{
perror(device);
return 1;
}
#ifndef _WIN32
struct termios options;
tcgetattr(fd, &options);
options.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
options.c_oflag &= ~(ONLCR | OCRNL);
tcsetattr(fd, TCSANOW, &options);
#endif
int position = maestroGetPosition(fd, 0);
printf("Current position is %d.\n", position);
int target = (position < 6000) ? 7000 : 5000;
printf("Setting target to %d (%d us).\n", target, target/4);
maestroSetTarget(fd, 0, target);
close(fd);
return 0;
}
If you want to use some USB device that is also recognised by the Apple FTDI serial driver, you can first unload the driver:
sudo kextunload -b com.apple.driver.AppleUSBFTDI
After that you can use it through libusb normally.
For other devices that are recognised as serial devices, you probably need to unload some other driver.
The problem seems to be due to a conflict between different drivers, which use the same libraries, and in my case they were related to previous Samsung device installations. i had solved this way:
kextstat | grep -v apple
To have a return like this:
70 0 0x57574000 0x3000 0x2000 com.devguru.driver.SamsungComposite (1.2.4) <33 4 3>
72 0 0x57831000 0x7000 0x6000 com.devguru.driver.SamsungACMData (1.2.4) <71 33 5 4 3>
94 0 0x57674000 0x3000 0x2000 com.devguru.driver.SamsungACMControl (1.2.4) <33 4 3>
Then:
$ sudo kextunload -b com.devguru.driver.SamsungComposite
$ sudo kextunload -b com.devguru.driver.SamsungACMData
$ sudo kextunload -b com.devguru.driver.SamsungACMControl
Done. Enjoy

tun program not creating device node

I'm following http://backreference.org/2010/03/26/tuntap-interface-tutorial/
The following code successfully gets a fd (usually 3) when I run it as root, but it does not create a /dev/tun77 device.
Should it?
#include <stdio.h>
#include <stdlib.h>
#include <net/if.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <string.h>
#include <linux/if_tun.h>
#include <linux/ioctl.h>
#define IFNAMSIZ 16
int tun_alloc(char *dev, int flags) {
struct ifreq ifr;
int fd, err;
char *clonedev = "/dev/net/tun";
/* Arguments taken by the function:
*
* char *dev: the name of an interface (or '\0'). MUST have enough
* space to hold the interface name if '\0' is passed
* int flags: interface flags (eg, IFF_TUN etc.)
*/
/* open the clone device */
if( (fd = open(clonedev, O_RDWR)) < 0 ) {
return fd;
}
/* preparation of the struct ifr, of type "struct ifreq" */
memset(&ifr, 0, sizeof(ifr));
ifr.ifr_flags = flags; /* IFF_TUN or IFF_TAP, plus maybe IFF_NO_PI */
if (*dev) {
/* if a device name was specified, put it in the structure; otherwise,
* the kernel will try to allocate the "next" device of the
* specified type */
strncpy(ifr.ifr_name, dev, IFNAMSIZ);
}
/* try to create the device */
if( (err = ioctl(fd, TUNSETIFF, (void *) &ifr)) < 0 ) {
close(fd);
return err;
}
/* if the operation was successful, write back the name of the
* interface to the variable "dev", so the caller can know
* it. Note that the caller MUST reserve space in *dev (see calling
* code below) */
strcpy(dev, ifr.ifr_name);
/* this is the special file descriptor that the caller will use to talk
* with the virtual interface */
return fd;
}
int main(void) {
char tun_name[IFNAMSIZ];
int nread, tun_fd;
char buffer[2048];
/* Connect to the device */
strcpy(tun_name, "tun77");
tun_fd = tun_alloc(tun_name, IFF_TUN | IFF_NO_PI); /* tun interface */
if (tun_fd < 0){
perror("Allocating interface");
exit(1);
} else {
printf("connected to %s on fd: %i\n", tun_name, tun_fd);
}
/* Now read data coming from the kernel */
while (1) {
/* Note that "buffer" should be at least the MTU size of the interface, eg 1500 bytes */
nread = read(tun_fd, buffer, sizeof(buffer));
if (nread < 0) {
perror("Reading from interface");
close(tun_fd);
exit(1);
}
/* Do whatever with the data */
printf("Read %d bytes from device %s\n", nread, tun_name);
}
return EXIT_SUCCESS;
}
Waldner answered this on http://backreference.org/2010/03/26/tuntap-interface-tutorial/ with:
Neither. Network interfaces in Linux don't appear under /dev; the only thing you'll see there is /dev/net/tun, which is the device that should be opened as the first step to create a tun/tap interface.
If you run the sample code, you'll be able to see and configure the interface you create by using "ip link" while the program is running; when the program terminates, the interface disappears.
Alternatively, the interface can be made persistent, as explained, and in that case it will survive program termination.
In any case, no device is created under /dev (apart from the already mentioned /dev/net/tun).

Add arp entry Linux

Read the question carefully in order to propose a solution, please
I need to add permanent arp entry in Linux somehow.
The problem is: if I add an entry via shell, or via sockets, it always gets flag 0x6. Even if I use the code posted downhere, where I specify the flag, it remains the same, 0x6.
I found this information about 0x6 flag:
Notice the ARP flag of "0x6". The ASIC ARP entry with flag 0x6 is
MAC-cache related entry. It is caused by arp lookup failure when
installing the session. The session will try to use the source MAC
address of incoming packet, but it is not necessary for using this mac
address. We can get the MAC address when the reply packet arrives by
sending an ARP packet to the source host.
So anytime I add any arp entry, then I ping the same ip address, it always results in ARP request broadcast.
The question is, is there a way how to add a permanent ARP entry with proper flag? So I add an entry, and in case of any comunication afterwards, there wont be any ARP broadcast?
Btw, to get into what I am up to: I am sending a broadcast(L3) from PC1 containing PC1's IP and MAC, PC2 gets the packet and add addresses them into ARP table and establish TCP session, but always first run ARP broadcast.
via shell:
#!/bin/sh
arp -s $1 $2 2>/dev/null
via sockets:
char *mac_ntoa(unsigned char *ptr){
static char address[30];
sprintf(address, "%02X:%02X:%02X:%02X:%02X:%02X",
ptr[0], ptr[1], ptr[2], ptr[3], ptr[4], ptr[5]);
return(address);
} /* End of mac_ntoa */
int mac_aton(char *addr, unsigned char *ptr){
int i, v[6];
if((i = sscanf(addr, "%x:%x:%x:%x:%x:%x", &v[0], &v[1], &v[2], &v[3],
&v[4], &v[5])) !=6){
fprintf(stderr, "arp: invalid Ethernet address '%s'\n", addr);
return(1);
} /* End of If*/
for(i = 0; i < 6; i++){
ptr[i] = v[i];
} /* End of For */
return(0);
}
int main(int argc, char* argv[]){
if(argc < 3 || argc > 4){
fprintf(stderr,"usage: %s <ip_addr> <hw_addr> [temp|pub|perm|trail]\n",
argv[0]);
fprintf(stderr, "default: temp.\n");
exit(-1);
} /* End of If */
int s, flags;
char *host = argv[1];
struct arpreq req;
struct hostent *hp;
struct sockaddr_in *sin;
bzero((caddr_t)&req, sizeof(req)); /* caddr_t is not really needed. */
sin = (struct sockaddr_in *)&req.arp_pa;
sin->sin_family = AF_INET;
sin->sin_addr.s_addr = inet_addr(host);
if(sin->sin_addr.s_addr ==-1){
if(!(hp = gethostbyname(host))){
fprintf(stderr, "arp: %s ", host);
herror((char *)NULL);
return(-1);
} /* End of If */
bcopy((char *)hp->h_addr,
(char *)&sin->sin_addr, sizeof(sin->sin_addr));
} /* End of If */
if(mac_aton(argv[2], req.arp_ha.sa_data)){ /* If address is valid... */
return(-1);
}
argc -=2;
argv +=2;
flags = ATF_PERM | ATF_COM;
while(argc-- > 0){
if(!(strncmp(argv[0], "temp", 4))){
flags &= ~ATF_PERM;
} else if(!(strncmp(argv[0], "pub", 3))){
flags |= ATF_PUBL;
} else if(!(strncmp(argv[0], "trail", 5))){
flags |= ATF_USETRAILERS;
} else if(!(strncmp(argv[0], "dontpub", 7))){ /* Not working yet */
flags |= ATF_DONTPUB;
} else if(!(strncmp(argv[0], "perm", 4))){
flags = ATF_PERM;
} else {
flags &= ~ATF_PERM;
} /* End of Else*/
argv++;
}/* End of While */
req.arp_flags = flags; /* Finally, asign the flags to the structure */
strcpy(req.arp_dev, "eth0"); /* Asign the device. */
if((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0){
perror("socket() failed.");
exit(-1);
} /* End of If */
if(ioctl(s, SIOCSARP, (caddr_t)&req) <0){ /* caddr_t not really needed. */
perror(host);
exit(-1);
} /* End of If */
printf("ARP cache entry successfully added.\n");
close(s);
return(0);
}
0x06 flag value means the entry is complete and permanent. So I guess your shell script is good enough to add a static arp entry. Here is the relevant flag values -
#define ATF_COM 0x02 /* completed entry (ha valid) */
#define ATF_PERM 0x04 /* permanent entry */
The definition of flag 0x06 that you posted is not related to the linux kernel.
The reason you're seeing an arp request may be due to problems in your topology or IP addressing. Can you post those details? Or you could post the packet trace where PC2 does an arp request even when it has a static arp entry.

Resources