Tom Herbert wrote on Fri, Aug 03, 2018:
> On Fri, Aug 3, 2018 at 4:20 PM, Dominique Martinet
> <asmad...@codewreck.org> wrote:
> > Tom Herbert wrote on Fri, Aug 03, 2018:
> >> struct my_proto {
> >>    struct _hdr {
> >>        uint32_t len;
> >>     } hdr;
> >>     char data[32];
> >> } __attribute__((packed));
> >>
> >> // use htons to use LE header size, since load_half does a first convertion
> >> // from network byte order
> >> const char *bpf_prog_string = " \
> >> ssize_t bpf_prog1(struct __sk_buff *skb) \
> >> { \
> >>     return bpf_htons(load_half(skb, 0)) + 4; \
> >> }";
> >
> > (Just to make sure I did fix it to htonl(load_word()) and I can confirm
> > there is no difference)
> 
> You also need to htonl for
> 
> my_msg.hdr.len = (i++ * 1312739ULL) % 31 + 1;

Thanks, but this looks correct to me - I was writing the header in
little endian order here and doing the double-swap dance in the bpf prog
because the protocol I was considering making a KCM implementation for
uses that.

Just to make sure, I rewrote it using network byte order e.g. these
three points and this makes no difference:
---8<----------------------
diff --git a/kcm.c b/kcm.c
index cb48df1..d437226 100644
--- a/kcm.c
+++ b/kcm.c
@@ -36,7 +36,7 @@ struct my_proto {
 const char *bpf_prog_string = "                                \
 ssize_t bpf_prog1(struct __sk_buff *skb)               \
 {                                                      \
-       return bpf_htons(load_half(skb, 0)) + 4;        \
+       return load_word(skb, 0) + 4;                   \
 }";
 
 int servsock_init(int port)
@@ -110,13 +110,15 @@ void client(int port)
 
        int i = 1;
        while(1) {
-               my_msg.hdr.len = (i++ * 1312739ULL) % 31 + 1;
-               for (int j = 0; j < my_msg.hdr.len; ) {
-                       j += snprintf(my_msg.data + j, my_msg.hdr.len - j, 
"%i", i - 1);
+               int len = (i++ * 1312739ULL) % 31 + 1;
+               my_msg.hdr.len = htonl(len);
+               for (int j = 0; j < len; ) {
+                       j += snprintf(my_msg.data + j, len - j,
+                                     "%i", i - 1);
                }
-               my_msg.data[my_msg.hdr.len-1] = '\0';
-               //printf("%d: writing %d\n", i-1, my_msg.hdr.len);
-               len = write(s, &my_msg, sizeof(my_msg.hdr) + my_msg.hdr.len);
+               my_msg.data[len-1] = '\0';
+               //printf("%d: writing %d\n", i-1, len);
+               len = write(s, &my_msg, sizeof(my_msg.hdr) + len);
                if (error == -1)
                        err(EXIT_FAILURE, "write");
                //usleep(10000);
@@ -171,9 +173,10 @@ void process(int kcmfd)
                len = recvmsg(kcmfd, &msg, 0);
                if (len == -1)
                        err(EXIT_FAILURE, "recvmsg");
-               if (len != my_msg.hdr.len + 4) {
+               if (len != ntohl(my_msg.hdr.len) + 4) {
                        printf("Got %d, expected %d on %dth message: %s; flags:
                        %x\n",
-                              len - 4, my_msg.hdr.len, i, my_msg.data, 
msg.msg_flags);
+                              len - 4, ntohl(my_msg.hdr.len), i,
+                              my_msg.data, msg.msg_flags);
                        exit(1);
                }
                i++;
----------------8<-----------

Frankly I do not believe this is a rule problem, as if the length
splitting was incorrect the program would not work at all, but just
uncommenting the usleep on the sender side makes this work.

Actually, now I'm looking closer to the timing, it looks specific to the
connection setup. This send loop works:
        int i = 1;
        while(i <= 1000) {
                int len = (i++ * 1312739ULL) % 31 + 1;
                my_msg.hdr.len = htonl(len);
                for (int j = 0; j < len; ) {
                        j += snprintf(my_msg.data + j, len - j,
                                      "%i", i - 1);
                }
                my_msg.data[len-1] = '\0';
                //printf("%d: writing %d\n", i-1, len);
                len = write(s, &my_msg, sizeof(my_msg.hdr) + len);
                if (error == -1)
                        err(EXIT_FAILURE, "write");
                if (i == 2)
                        usleep(1);
        }

But removing the usleep(1) after the first packet makes recvmsg()
"fail": it reads the content of the second packet with the size of the
first.


I assume that usleep gives the server time to finish setting up the kcm
socket, because it does accept(); ioctl(SIOCKCMATTACH); recvmsg(); but
the client does not wait to send packets so there could be some sort of
race with the attach and multiple packets?


FWIW I took the time to look at older kernel and this has been happening
ever since KCM got introduced in 4.6


Thanks,
-- 
Dominique

Reply via email to