Set CONNMARK in netfilter module - linux-kernel

I have a netfilter module which hooks into NF_INET_LOCAL_OUT. In the hook function, I set the mark as follows:
if (tcp_header->syn && dport == 80) {
skb->mark = 0x1;
}
However, this seems to set the mark only for this packet and not the connection mark.
How do I set the connmark to 0x1 so that all packets in this flow
have this mark?
How do I access connmark for a packet in the
hook function?
Thanks for your help.

NB: I am not a kernel network guru; I'm just good at reading the network code itself :)
Cribbing entirely from net/netfilter/xt_connmark.c in the latest version (4.15.8), try the following code:
if (tcp_header->syn && dport == 80) {
skb->mark = 0x1;
enum ip_conntrack_info ctinfo;
struct nf_conn *ct;
ct = nf_ct_get(skb, &ctinfo);
if (ct != NULL) {
u_int32_t newmark;
newmark = 0x01;
if (ct->mark != newmark) {
ct->mark = newmark;
nf_conntrack_event_cache(IPCT_MARK, ct);
}
}
}
Basically, in order to set the CONNMARK itself, you need to first get the actual conntrack entry for the flow. Once you've done that, you see if the current mark is already set to your new mark of 0x01. If it isn't, you set the mark and fire an event that the mark has been set.
You may want to look a bit more into connmark_tg, which is where this snippet is modified from; it may give you a bit more insight than just this code block alone.
Best of luck!

Related

Is there a way to read the NET_BUFFER at once?

I made NDIS 6 network filter driver and am reading the packet.
When I use Intel I350 NIC, 'MmGetMdlByteCount' returns '9014'bytes.
This value is the same as the MTU size, so I can read the data at once.
However, when using the x540 NIC, 'MmGetMdlByteCount' is returned to '2048'bytes.
So I have to read the MDL over and over again. Why is this happening?
Is there a way to read data at once on the X540 NIC?
I want to reduce repetition because I think the consumption time will be longer if I bring the data several times.
Below is a part of my source code.
PVOID vpByTmpData = NULL;
for( pNbMdl = NET_BUFFER_CURRENT_MDL( pNetBuffer );
pNbMdl != NULL && ulDataLength > 0;
pNbMdl = NDIS_MDL_LINKAGE( pNbMdl ) )
{
ulBytesToCopy = MmGetMdlByteCount( pNbMdl );
if( ulBytesToCopy == 0 )
continue;
vpByTmpData = MmGetSystemAddressForMdlSafe( pNbMdl, NormalPagePriority );
if( !vpByTmpData )
{
bRet = FALSE;
__leave;
}
if( ulBytesToCopy > ulDataLength )
ulBytesToCopy = ulDataLength;
NdisMoveMemory( &baImage[ulMemIdxOffset], (PBYTE)(vpByTmpData), ulBytesToCopy);
ulMemIdxOffset += ulBytesToCopy;
}
Please help me.
What you're seeing is a result of how the NIC hardware physically works. Different hardware will use different buffer layout strategies. NDIS does not attempt to force every NIC to use the same strategy, since that would reduce performance on some NICs. Unfortunately for you, that means the complexity of dealing with different buffers gets pushed upwards into NDIS filter & protocol drivers.
You can use NdisGetDataBuffer to do some of this work for you. Internally, NdisGetDataBuffer works like this:
if MmGetSystemAddressForMdl fails:
return NULL;
else if the payload is already contiguous in memory:
return a pointer to that directly;
else if you provided your own buffer:
copy the payload into your buffer
return a pointer to your buffer;
else:
return NULL;
So you can use NdisGetDataBuffer to obtain a contiguous view of the payload. The simplest way to use it is this:
UCHAR ScratchBuffer[MAX_MTU_SIZE];
UCHAR *Payload = NdisGetDataBuffer(NetBuffer, NetBuffer->DataLength, ScratchBuffer, 1, 0);
if (!Payload) {
return NDIS_STATUS_RESOURCES; // very unlikely: MmGetSystemAddressForMdl failed
}
memcpy(baImage, Payload, NetBuffer->DataLength);
But this can have a double-copy in some cases. (Exercise to test your understanding: when would there be a double-copy?) For slightly better performance, you can avoid the double-copy with this trick:
UCHAR *Payload = NdisGetDataBuffer(NetBuffer, NetBuffer->DataLength, baImage, 1, 0);
if (!Payload) {
return NDIS_STATUS_RESOURCES; // very unlikely: MmGetSystemAddressForMdl failed
}
// Did NdisGetDataBuffer already copy the payload into my flat buffer?
if (Payload != baImage) {
// If not, copy from the MDL to my flat buffer now.
memcpy(baImage, Payload, NetBuffer->DataLength);
}
You haven't included a complete code sample, but I suspect there may be some bugs in your code. I don't see any attempt to handle NetBuffer->CurrentMdlOffset. While this is usually zero, it's not always zero, so your code would not always be correct.
Similarly, it looks like the copy isn't correctly constrained by ulDataLength. You would need a ulDataLength -= ulBytesToCopy in there somewhere to fix this.
I'm very sympathetic to how tricky it is to navigate NBLs, NBs, and MDLs -- my first NIC driver included a nasty bug in calculating MDL offsets. I have some MDL-handling code internally -- I will try to clean it up a bit and publish it at https://github.com/microsoft/ndis-driver-library/ in the next few days. I'll update this post if I get it published. I think there's clearly a need for some nice, reusable and well-documented sample code to just copy (subsets of) an MDL chain into a flat buffer, or vice-versa.
Update: Refer to MdlCopyMdlChainAtOffsetToFlatBuffer in mdl.h

winsock2: How to get the ipv4/ipv6 address of a connected client after server side code calls `accept()`

There are other similar questions on this site, but they either do not related to winsock2 or they are suitable only for use with ipv4 address spaces. The default compiler for Visual Studio 2019 produces an error when the ntoa function is used, hence an ipv4 and ipv6 solution is required.
I did once produce the code to do this for a Linux system however I am currently at work and do not have access to that. It may or may not be "copy and paste"-able into a windows environment with winsock2. (Edit: I will of course add that code later this evening, but of course it might not be useful.)
The following contains an example, however this is an example for client side code, not server side code.
https://www.winsocketdotnetworkprogramming.com/winsock2programming/winsock2advancedInternet3c.html
Here, the getaddrinfo() function is used to obtain a structure containing matching ipv4 and ipv6 addresses. To obtain this information there is some interaction with DNS, which is not required in this case.
I have some server code which calls accept() (after bind and listen) to accept a client connection. I want to be able to print the client ip address and port to stdout.
The most closely related question on this site is here. However the answer uses ntoa and is only ipv4 compatible.
What I have so far:
So far I have something sketched out like this:
SOCKET acceptSocket = INVALID_SOCKET;
SOCKADDR_IN addr; // both of these are NOT like standard unix sockets
// I don't know how they differ and if they can be used with standard
// unix like function calls (eg: inet_ntop)
int addrlen = sizeof addr;
acceptSocket = accept(listenSocket, (SOCKADDR*)&addr, &addrlen);
if(acceptSocket == INVALID_SOCKET)
{
// some stuff
}
else
{
const std::size_t addrbuflen = INET6_ADDRSRTLEN;
char addrbuf[addrbuflen] = '\0'
inet_ntop(AF_INET, (void*)addr.sin_addr, (PSTR)addrbuf, addrbuflen);
// above line does not compile and mixes unix style function calls
// with winsock2 structures
std::cout << addrbuf << ':' << addr.sin_port << std::endl;
}
getpeername()
int ret = getpeername(acceptSocket, addrbuf, &addrbuflen);
// addrbuf cannot convert from char[65] to sockaddr*
if(ret == ???)
{
// TODO
}
You need to access the SOCKADDR. This is effectively a discriminated union. The first field tells you whether its an IPv4 (==AF_INET) or IPv6 (==AF_INET6) address. Depending on that you cast the addr pointer to be either struct sockaddr_in* or struct sockaddr_in6*, and then read off the IP address from the relevant field.
C++ code snippet in vs2019:
char* CPortListener::get_ip_str(struct sockaddr* sa, char* s, size_t maxlen)
{
switch (sa->sa_family) {
case AF_INET:
inet_ntop(AF_INET, &(((struct sockaddr_in*)sa)->sin_addr),
s, maxlen);
break;
case AF_INET6:
inet_ntop(AF_INET6, &(((struct sockaddr_in6*)sa)->sin6_addr),
s, maxlen);
break;
default:
strncpy(s, "Unknown AF", maxlen);
return NULL;
}
return s;
}
Example:
{
...
char s[INET6_ADDRSTRLEN];
sockaddr_storage ca;
socklen_t al = sizeof(ca);
SOCKET recv = accept(sd, (sockaddr*)&ca, &al);
pObj->m_ip = get_ip_str(((sockaddr*)&ca),s,sizeof(s));
}

What is sin6_scope_id for the IPv6 loopback address?

I want to create a socket bound to any port on the local machine so I can simulate socketpair() on Windows. When I want this socket to be IPv6, what value should I set for sin6_scope_id? Do I actually have to enumerate the adapters and find the loopback adapter in order to fill in that field?
In other words, what do I need to do with this in order to bind to any local port?
struct sockaddr_in6 addr;
addr.sin6_family = AF_INET6;
addr.sin6_port = htons(0);
addr.sin6_flowinfo = 0;
addr.sin6_addr = in6addr_loopback;
addr.sin6_scope_id = ????;
Some socket gurus might know a direct way to set it. But it's easier to just query the system for it. This will correctly set both "flowinfo" and "scope_id" for you.
General approach is this:
Invoke getifaddrs to get a list of ifaddrs instances.
Enumerate the list of ifaddrs returned from this api until you find the AF_INET6 adapter with the IFF_LOOPBACK bit set on the ifa_flags member. This is the adapter you want to bind to.
Now that you have a pointer to the ifaddrs instance, cast its ifa_addr member to a (sockaddr_in6*). Copy this structure into your own addr and set the port.
Some sample code below:
struct sockaddr_in6 addr = {0};
ifaddrs* pList = NULL;
ifaddrs* pAdapterFound = NULL;
ifaddrs* pAdapter = NULL;
getifaddrs(&pList);
pAdapter = pList;
while (pAdapter)
{
if ((pAdapter->ifa_addr != NULL) &&
(family == pAdapter->ifa_addr->sa_family == AF_INET6) &&
(pAdapter->ifa_flags & IFF_LOOPBACK))
{
pAdapterFound = pAdapter;
break;
}
pAdapter = pAdapter->ifa_next;
}
if (pAdapterFound)
{
// C++ way to copy the sockaddr_in6 struct
addr = *(reinterpret_cast<sockaddr_in6*>(pAdapterFound->ifa_addr)); // C++
// If you are coding in C instead of C++
// memcpy(&addr, pAdapterFound->ifa_addr, sizeof(addr));
addr.sin6_port = htons(0); // or whatever port you want to bind to
}
freeifaddrs(pList);
You are welcome to reference a helper function I have called GetSocketAddressForAdapter. You could invoke it as follows:
GetSocketAddressforAdapter(AF_INET6, "::1", 0, &address);

Writing SSPBUF from variable in I2C slave protocol in PIC18

I am writing an I2C slave routine for PIC18F25K80 and I am stuck on a weird problem.
This is my routine:
void interrupt interruption_handler() {
PIE1bits.SSPIE = 0; // Disable Master Synchronous Serial Port Interrupt
if (PIR1bits.SSPIF != 1) {
//This is not I2C interruption;
PIE1bits.SSPIE = 1; // Enable Master Synchronous Serial Port Interrupt
return;
}
//Treat overflow
if ((SSPCON1bits.SSPOV) || (SSPCON1bits.WCOL)) {
dummy = SSPBUF; // Read the previous value to clear the buffer
SSPCON1bits.SSPOV = 0; // Clear the overflow flag
SSPCON1bits.WCOL = 0; // Clear the collision bit
SSPCON1bits.CKP = 1;
board_state = BOARD_STATE_ERROR;
} else {
if (!SSPSTATbits.D_NOT_A) {
//Slave address
debug(0, ON);
//Read address
address = SSPBUF; //Clear BF
while(BF); //Wait until completion
if (SSPSTATbits.R_NOT_W) {
SSPCON1bits.WCOL = 0;
unsigned char a = 0x01;
SSPBUF = a;//0x01 works //Deliver first byte
asm("nop");
}
} else {
if (SSPSTATbits.BF) {
dummy = SSPBUF; // Clear BF (just in case)
while(BF);
}
if (SSPSTATbits.R_NOT_W) {
//Multi-byte read
debug(1, ON);
SSPCON1bits.WCOL = 0;
SSPBUF = 0x02; //Deliver second byte
asm("nop");
} else {
//WRITE
debug(2, ON);
}
}
transmitted = TRUE;
SSPCON1bits.CKP = 1;
PIR1bits.SSPIF = 0;
PIE1bits.SSPIE = 1; // Enable Master Synchronous Serial Port Interrupt
}
}
It works like a charm if I set constant values on SSPBUF. For example, if you do:
SSPBUF = 0x01;
(...)
SSPBUF = 0x02;
I get the two bytes on the master. I can even see the wave forms of the bytes being transmitted on the oscilloscope. Quite fun!
But when I try to set SSPBUF using a variable like:
unsigned char a = 0x01;
SSPBUF = a;
I get zero on the master.
It is driving me crazy.
Some hypothesis I've discarded:
Watchdog timer is messing up interrupting in the middle of the protocol: It is not. It is disabled and the problem happens in both SSPBUF assignments
I need to wait until BF goes low to continue: I don't. AFAIK, you setup the SSPBUF, clear SSPIF, set CKP and return from interruption to take care of life in 4Mhz while the hardware send data in few Khz. It will interrupt you again when it finishes.
It makes no sense to me. How good it is if you cannot define an arbitrary value using a variable?
Please gurus out there, enlighten this poor programmer.
Thanks in advance.
It has something to do with how the compiler generates the code and some undocumented/unknown PIC restriction around SSPBUF (it is an special register anyway).
I found out that it works when the compiler uses movwf and does not work when the compiler uses movff.
I moved the question to another forum because I realized the audience there is more adequate.
You will find more details here:
https://electronics.stackexchange.com/questions/251763/writing-sspbuf-from-variable-in-i2c-slave-protocol-in-pic18/251771#251771
Try move declaration : "unsigned char a = 0x01;"
to the beginning of the function or try define it as volatile global variable.
take into accunte that SSPBUF is both read and write buffer.check if there are conditions that may cause I2C module to reset this buffer.

Help needed with F_NOCACHE in mac

I am Srinivasa Raghavan and new to this group.
I am facing problem with non caching the file.
the code looks like the below:
main()
{
int fd;
char buf[512] = {'\0'};
fd = fopen("Sample.bin",O_RDONLY);
fcntl(fd, F_NOCACHE, 1);
fcntl(fd, F_RDAHEAD, 1);
read(fd, buf, sizeof(buf));
close(fd);
if(buf[0] == 'x' )
print("non-cached\n");
else
printf("cached\n")
}
the problem was, the F_NOCACHE doesn't not work properly, and all the time I get the message cached only. The firware will always update the value 'x' in sample.bin.
the above code works if I put the entire stuff (open, fcntl, read and close) in an indefinite loop (take long time to come out) like the below.
main()
{
while(1)
{
open...
fcntl(.., F_NOCACHE)
read(....
close..
if(buf[0] == 'x')
break;
}
}
I am really stuck with this for a week time, I want to know the exact behaviour of F_NOCACHE, and any information will be highly appreciated.
Thanks in advance,
Srinivasa Raghavan
That's not what it's for. F_NOCACHE tells the system that you don't expect to read that data* off the disk again any time soon, so it shouldn't bother caching it.
*Yes, "data" is plural, I know.

Resources