ProgrammingThis forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.
Notices
Welcome to LinuxQuestions.org, a friendly and active Linux Community.
You are currently viewing LQ as a guest. By joining our community you will have the ability to post topics, receive our newsletter, use the advanced search, subscribe to threads and access many other special features. Registration is quick, simple and absolutely free. Join our community today!
Note that registered members see fewer ads, and ContentLink is completely disabled once you log in.
If you have any problems with the registration process or your account login, please contact us. If you need to reset your password, click here.
Having a problem logging in? Please visit this page to clear all LQ-related cookies.
Get a virtual cloud desktop with the Linux distro that you want in less than five minutes with Shells! With over 10 pre-installed distros to choose from, the worry-free installation life is here! Whether you are a digital nomad or just looking for flexibility, Shells can put your Linux machine on the device that you want to use.
Exclusive for LQ members, get up to 45% off per month. Click here for more info.
I have made a function that I am using in a Kernel Module. The fucntion reads the data field of the TCP option you want. It will either return the data value as a 64-bit integer, return 1 if the option is present, but has no data, or return 0 if the option is not present.
I am testing with the TCP Option for MSS ie option 2. Its length is 4 bytes. Byte1 = its option number 2, Byte 2 = its length 4, and Byte 3&4 are a 16-bit integer value. Default is 1460.
If I request this option value with my function it returns "1460", but then I set it to 1460 - 60 or 1400 WireShark shows that its set correctly, but when I try to read it again with my fuction it returns "1404"
Now I tried setting it to 1460 - 64, and behold it works correctly again. So what could cause this to return incorrect data for these specific values?
Edit:
I tried a whole bunch of different data values, and 1400 is the only one with the problem so far.
1460 OK = 1460
1400 BAD = 1404
1396 OK = 1396
1300 OK = 1300
1404 OK = 1404
1340 OK = 1340
Code:
#include <linux/autoconf.h>
#include <linux/netdevice.h>
#include <linux/if_ether.h>
#include <linux/skbuff.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#include <net/protocol.h>
#include <net/pkt_sched.h>
#include <net/ip.h>
#include <net/tcp.h> /* Needed for tcpmagic and TCP Options */
static inline unsigned int
optlen(const u_int8_t *opt, unsigned int offset)
{
if (opt[offset] <= TCPOPT_NOP || opt[offset+1] == 0)
return 1;
else
return opt[offset+1];
}
/*
* yaplej: This function will attempt to locate
* the requested tcp option from the passed skb.
*
* If it locates the tcp option it will then check its length
* to determine if there is any option data to return.
* If there is option data it will return that, and
* if there is not data for the option it will return 1.
*
* If the option is not found it will return 0.
*/
static inline u_int64_t
__get_tcp_option(struct sk_buff *skb, unsigned int tcpoptnum)
{
unsigned int i, tcpoptlen;
struct tcphdr *tcph;
struct iphdr *iph;
u8 *opt;
iph = ip_hdr(skb);
tcph = (struct tcphdr *)(skb_network_header(skb) + ip_hdrlen(skb));
opt = (u_int8_t *)tcph + sizeof(struct tcphdr);
for (i = 0; i < tcph->doff*4 - sizeof(struct tcphdr); i += optlen(opt, i)) {
if (opt[i] == tcpoptnum) {
tcpoptlen = opt[i+1]; // get option length.
if ((tcph->doff*4 - i >= tcpoptlen) &&
(opt[i+1] == tcpoptlen)) {
u_int64_t tcpoptdata;
u_int8_t bytefield;
u_int8_t count;
/* if the option exists, but data length is 2 */
if (tcpoptlen == 2)
return 1;
else
count = tcpoptlen - 2; // get option length,
// and ignore header fields.
bytefield = 2; // the first data byte is always at i+2.
tcpoptdata = 0; // initialize tcpoptdata for compiler.
while (count > 0) {
count--;
if ((count) != 0) {
tcpoptdata += (opt[i+bytefield] << 8 * count);
}
else {
tcpoptdata += opt[i+bytefield];
}
bytefield++;
}
tcpoptdata |= opt[i+bytefield] & 0xFE;
return tcpoptdata;
}
}
}
return 0;
}
Excessive indenting helps me debug, and easier for me to read. It’s taken a week to get my two functions semi-working and not crashing the kernel.
Opt is set correctly, and this works 99% of the time it’s just where the data field = 1400 where it’s not working.
Well at least that I have found so far. Because the data field can be between 0-bits to 64-bit I have not tested every possibility, but I have only found this one for sure that it’s screwed up.
Its reading two bytes that as an integer = 1400 the byte values are 00000101 & 01111000 if I understand the code correctly the first byte is read as 5, and then left shifted 8 places turning 5 into 1280. Then the second (and last in this case) byte field is read as 120, and added to tcpoptdata value. So at this point it should = 1400.
Next I do this, and honestly I don’t know why, but it was in my example so I left it in there.
Distribution: M$ Windows / Debian / Ubuntu / DSL / many others
Posts: 2,339
Rep:
i think i gave that to you
& 0xFE repairs an error with the one'th bit
and will not cause your problem
but it assumes opt is 1 byte in size
you could use the cheater way out by using an if statement
so if you request 1400 it will manually correct the error
I am not sure what the one'th bit error is, but I definatly dont want to take the cheater way out, and manually correct it.
Opt is a pointer to the first byte of either the TCP options or the TCP data. If tcph->doff is > 5 its going to point to the first byte of TCP options if tcph->doff is == 5 then there are no TCP options, and its going to point at the first byte of TCP data. In that case I dont do anything with the options because there are none, and the function just returns 0 because no options exist in the TCP segment.
Well I took out that unknown bit of code, and it works now for the value of 1400, and has not broken the other values so far. I still need to do a little more validation to make sure its all ok, but it does not seem to be needed.
LinuxQuestions.org is looking for people interested in writing
Editorials, Articles, Reviews, and more. If you'd like to contribute
content, let us know.