arp_send: what is the difference between target_hw and dest_hw? - c

I am trying to generate arp requests from within the kernel but I do not understand the difference between the 'target MAC address' and the 'destination MAC address'. The kernel function that I am using is this one:
void arp_send(int type, int ptype, __be32 dest_ip,
struct net_device *dev, __be32 src_ip,
const unsigned char *dest_hw, const unsigned char *src_hw,
const unsigned char *target_hw)
Does anyone know the difference between 'target_hw' (the target MAC address) and 'dst_hw' (the destination MAC address)? For me they should be the same...

The arp_send function is a generic one, used to send both ARP requests and responses.
In your case (ARP request) the target_hw is the information you want to learn, so this field can be ignored (set to NULL, see RFC826 example)
dest_hw will also be NULL - which will result in using broadcast address (see arp_create comment)
I'm assuming IPv4 over Ethernet here. For other Layer2/3 protocols it might look different.

Related

Is there any way to detect packet direction in the PRE_ROUTING hook point

I'm trying to create a firewall in C as a linux kernel module. as part of the firewall, I've implemented a hook function which performs packets inspection inside the PRE_ROUTING hook point.
In the hook function I need to deduce the packet direction based on its source and destination networking devices.
Whenever I try to extract the source and destination devices, in the packet inspection function, a kernel panic occurs and the OS crashes, and I have no idea why (I've followed linux/netfilter.h strictly). I would more than appreciate any help!
The relevant part of the hook function is as below:
unsigned int inspect_packet(void *priv, struct sk_buff *skb, const struct nf_hook_state *state)
{
char *src_device;
char *dst_device;
src_device = state->in->name;
dst_device = state->in->name;
/* Deduce the packets direction by the networking devices direction */
if (src_device[5] == IN_DEVICE_NUM && dst_device[5] == OUT_DEVICE_NUM)
{
/* some code */
}
}
As you can see, I used (as in the header files) the state->in and state->out fields in order to extract the source and destination device of the packet.
Note: The kernel panic certainly occurs from the code above, the rest of the code is irrelevant.
Solution:
As found in the comments above, the mistake which was made is the assumption the destination device of the packet is already assigned when the hook function was called. The assumption is problematic because the hook function is registered in the PRE_ROUTING hook and therefore still has no destination networking device. In order to solve the problem, we can deduce the packet direction just from the source device.
Here is the fixed version of the code:
unsigned int inspect_packet(void *priv, struct sk_buff *skb, const struct nf_hook_state *state)
{
char *src_device;
src_device = state->in->name;
/* Deduce the packets direction just by the source networking device */
if (src_device[5] == IN_DEVICE_NUM)
{
/* some code */
}
}

Print MAC address from Ethernet header

I want to check the Ethernet header of packet that is porececed by iptables (1.4), so i need to write a module that catch packet and apply my function. I am looking for the mac destination value in the Ethernet header (just for test purpose), so the code should be like this:
static bool match(const struct sk_buff *skb, struct xt_action_param *par)
{
struct ethhdr *hdr;
hdr = eth_hdr(skb);
printk(KERN_INFO "hdr->h_dest 0x%x\n", hdr->h_dest);
printk(KERN_INFO "MACPROTO=%04x\n", hdr->h_proto);
The problem is that i cant get the correct value, i have some thing that is not even in the real frame (i checked that with Wireshark), so is it the right function to get Ethernet header attributs?
update:
i used the solution presented in the post, but still have wrong output, it's like if the structure point to wrong place
This image show result when i use nc to send "aaa" string, the ethernet header should be the same in the to frame, but as present in the result, it's not.
struct ethhdr is defined as such:
/*
* This is an Ethernet frame header.
*/
struct ethhdr {
unsigned char h_dest[ETH_ALEN]; /* destination eth addr */
unsigned char h_source[ETH_ALEN]; /* source ether addr */
__be16 h_proto; /* packet type ID field */
} __attribute__((packed));
But your code is trying to print that byte array using %x:
printk(KERN_INFO "hdr->h_dest 0x%x\n", hdr->h_dest);
That makes no sense, and probably causes a compiler warning to be generated. This is kernel code - you are using -Werror and -Wall, right?
Good news: printk supports printing MAC addresses directly. From documentation/printk-formats.txt:
MAC/FDDI addresses:
%pM 00:01:02:03:04:05
%pMR 05:04:03:02:01:00
%pMF 00-01-02-03-04-05
%pm 000102030405
%pmR 050403020100
For printing 6-byte MAC/FDDI addresses in hex notation. The 'M' and 'm'
specifiers result in a printed address with ('M') or without ('m') byte
separators. The default byte separator is the colon (':').
Where FDDI addresses are concerned the 'F' specifier can be used after
the 'M' specifier to use dash ('-') separators instead of the default
separator.
For Bluetooth addresses the 'R' specifier shall be used after the 'M'
specifier to use reversed byte order suitable for visual interpretation
of Bluetooth addresses which are in the little endian order.
Passed by reference.
So you can just use this:
printk(KERN_INFO "hdr->h_dest 0x%pM\n", hdr->h_dest);
These format specifiers are provided anywhere vsnprintf is used. Here's an example:
switch (dev->type) {
case ARPHRD_ETHER:
nf_log_buf_add(m, "MACSRC=%pM MACDST=%pM MACPROTO=%04x ",
eth_hdr(skb)->h_source, eth_hdr(skb)->h_dest,
ntohs(eth_hdr(skb)->h_proto));
return;
default:
break;
}

Constructing a new IPv6 address from a Contiki uip address in C

I'm inexperienced with both Contiki and C but I'm trying to do the following:
Basically, I get a structure, event, which has a type, an id and a uip ip6address.
Using this event, I want to construct a uip ipv6 multicast address with a fixed prefix (ff1e).
At the moment I have the following code:
static uip_ds6_maddr_t *
derive_mcast_addr(struct eventstruc* event)
{
int ff1e;
//Fixed multicast prefix to be used by LooCI.
uint8_t mlcPrefix = ff1e;
//Type of the event
uint8_t eventType = event->type;
//Publisher Component ID of the sender
uint8_t * srccomp = event->source_cid;
// IPv6 address of the sender
uip_ip6addr_t * srcaddr = event->source_node);
// A derived multicast address is
// mlcPrefix + ":" + eventType + ":" +srccomp + ":0:" + (last 64bits srcAddr)
}
I'm unsure if this code is decent and on how to get the last 64 bits of the src address, especially since they might not be in the expected format.
For example, if the source address is 0::0:0:0:0 then I'd just need the 0:0:0:0 part. If it was, say, 2001::a00:27ff:fef7:30a7, I'd just need a00:27ff:fef7:30a7.
Also, there is the added complexity of Contiki uip...
Anybody have a decent idea?
First, your uint8_t variables are probably not wide enough, you might need:
//Fixed multicast prefix to be used by LooCI.
uint16_t mlcPrefix = 0xff1e;
I'm not familiar with Contiki, but based on this: http://dak664.github.io/contiki-doxygen/a00424_source.html uip_ip6addr_t is really this:
typedef union uip_ip6addr_t {
u8_t u8[16]; /* Initializer, must come first!!! */
u16_t u16[8];
} uip_ip6addr_t;
If that's the case, then you can get the lower 64 bits by looking at:
srcaddr->u16[4]
srcaddr->u16[5]
srcaddr->u16[6]
srcaddr->u16[7]
Or it could be indexes 0-3 depending on how things are stored in uip_ip6addr_t.
To put things back together, you can put your upper 64 bits in u16[0] through u16[3] and then put the original lower 64 bits back in u16[4] through u16[7].
If uip_ds6_maddr_t is this:
typedef struct uip_ds6_maddr {
uint8_t isused;
uip_ipaddr_t ipaddr;
} uip_ds6_maddr_t;
And you have a pointer uip_ds6_maddr_t *dst then you could do:
dst->ipaddr.u16[0] = mlcPrefix;
And so on.

method to change __be32 ip address into char in kernel space

I am making a module where I need to change the __be 32 format of address into char, which function could I use and under which header file it comes (I know to convert char to __be32 we use in_aton).
For kernels older than 2.6.26(if not mistaken) you need to use the NIPQUAD macro, like:
pritk("%d.%d.%d.%d\n", NIPQUAD(your_b32_address));
For newer kernels a switch to printk was added:
printk("%pI4\n", your_b32_address);
Have a look here: http://www.kernel.org/doc/htmldocs/kernel-hacking/common-routines.html
I would like to clarify that if you have
__be32 ipaddr;
you must pass the address of 'ipaddr' to printk
printk("%pI4\n", &ipaddr);

Get ip-address of local NIC from kernel module

I am writing a kernel module which forms its own packet at ip level and then sends the data . I just want to know how to find the ip address of a local network interface.
There are defined ioctls SIOCGIFADDR (to get if address) and SIOCSIFADDR (to set if address).
All device configuration for IPv4 is done in net/ipv4/devinet.c. You can refer to this source file to get more information.
Wanting to read interface addresses reeks of a design problem. However, if you are looking for determining the preferred source address to use when contacting a remote peer is given in struct rt6_info.rt6i_prefsrc after obtaining it with ip6_route_output. Something along the lines of (with no implied guarantees):
int pick_addr(struct in6_addr *saddr, struct net *net, const struct in6_addr *daddr)
{
struct rt6_info *rt;
struct flowi6 fl6;
int ret;
memset(&fl6, 0, sizeof(fl6));
memcpy(&fl6.daddr, daddr, sizeof(*daddr));
dst = (struct rt6_info *)ip6_route_output(net, NULL /* or sk if you have it */, &fl6);
ret = rt->dst.error;
if (ret == 0)
memcpy(saddr, &rt->rt6i_prefsrc.addr, sizeof(*saddr));
dst_release(&rt->dst);
return ret;
}
We have if_getconfig function available in linux-x.y.z/Documentation/networking/ifenslave.c file which is a very good example of how to use ioctls and fetch address from kernel space

Resources