I want to write a bpf program which return [1,n] in round-robin pattern. e.g if n=4 then it should return 1,2,3,4,1,2,3,4,1,2..
Algorithm for above idea is:
- A = read M[0]
- A = A % n-1
- A = A+1
- store M[0] = A
- return A
Apart from first step, everything is correct in my case. I am not able to load value from memory into accumulator.
BPF code
// CBPF code for REUSEPORT using plain round robin algorithm
struct sock_filter code[] = {
{BPF_LD | BPF_MEM, 0, 0, 0}, // A = M[0]
{BPF_ALU | BPF_MOD | BPF_K, 0, 0, groupSize - 1}, // A = A % group_size-1
{BPF_ALU | BPF_ADD | BPF_K, 0, 0, 1}, // A = A+1
{BPF_ST, 0, 0, 0}, // M[0] = A
{BPF_RET | BPF_A, 0, 0, 0} // return A
};
There is problem in using BPF_LD | BPF_MEM to load value from memory into accumulator. What is correct way to do this.
Here is my complete code, you can take and run:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <linux/filter.h>
#include <sys/types.h>
#include <errno.h>
#include <string.h>
#define BUFFER_SIZE 2048
#define EXIT_FAILURE 1
#define EXIT_SUCCESS 0
char ip[] = "0.0.0.0";
int port = 7155;
unsigned int groupSize = 3;
int sockFD;
void print_error(char* main_message)
{
printf("%s.\nErrNo: %d. ErrMessage: %s\n",main_message, errno, strerror(errno));
}
int udp_balancer()
{
struct sockaddr_in6 serverAddress;
// initialise socket
sockFD = socket(AF_INET6, SOCK_DGRAM, 0);
if (sockFD < 0)
{
print_error("Socket initialisation failed");
return EXIT_FAILURE;
}
// Enable SO_REUSEPORT option
const int enable = 1;
if (setsockopt(sockFD, SOL_SOCKET, SO_REUSEPORT, &enable, sizeof(int)) < 0)
{
print_error("setsockopt(SO_REUSEPORT) failed");
return EXIT_FAILURE;
}
// CBPF code for REUSEPORT dispatch based on CPU() % group_size
// struct sock_filter code[] = {
// {BPF_LD | BPF_W | BPF_ABS, 0, 0, SKF_AD_CPU}, // A = #cpu
// {BPF_ALU | BPF_MOD | BPF_K, 0, 0, groupSize - 1}, // A = A % group_size-1
// {BPF_ALU | BPF_ADD | BPF_K, 0, 0, 1}, // A = A+1
// {BPF_RET | BPF_A, 0, 0, 0} // return A
// };
// CBPF code for REUSEPORT using plain round robin algorithm
struct sock_filter code[] = {
{BPF_LD | BPF_MEM, 0, 0, 0}, // A = M[0]
{BPF_ALU | BPF_MOD | BPF_K, 0, 0, groupSize - 1}, // A = A % group_size-1
{BPF_ALU | BPF_ADD | BPF_K, 0, 0, 1}, // A = A+1
{BPF_ST, 0, 0, 0}, // M[0] = A
{BPF_RET | BPF_A, 0, 0, 0} // return A
};
struct sock_fprog bpf = {
.len = sizeof(code) / sizeof(code[0]),
.filter = code,
};
// Attach bpf program
socklen_t sizeOfBPF = (socklen_t)sizeof(bpf);
if (setsockopt(sockFD, SOL_SOCKET, SO_ATTACH_REUSEPORT_CBPF, &bpf, sizeOfBPF) < 0)
{
print_error("Bpf attach is failed");
return EXIT_FAILURE;
}
// Configure serverAddress object
memset(&serverAddress, 0, sizeof(serverAddress));
serverAddress.sin6_family = AF_INET6; // address is of type ip6
// serverAddress.sin6_addr = in6addr_any;
inet_pton(AF_INET6, ip, &(serverAddress.sin6_addr));
serverAddress.sin6_port = htons(port);
// Bind socket to specific ip and port
if (bind(sockFD, (struct sockaddr *)&serverAddress, sizeof(serverAddress)) < 0)
{
print_error("Address binding is failed");
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
void displayConfig()
{
pid_t pid = getpid();
printf("FD %d PID %d\n",sockFD,pid);
struct sockaddr_in addr;
socklen_t addrLen = sizeof(addr);
// Assuming sockfd is your bound UDP socket
if (getsockname(sockFD, (struct sockaddr *)&addr, &addrLen) == -1)
{
print_error("getsockname failed");
exit(EXIT_FAILURE);
}
char ipStr[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &(addr.sin_addr), ipStr, INET_ADDRSTRLEN);
printf("Listening on IP: %s::%d\n" , ipStr ,ntohs(addr.sin_port));
}
void capture()
{
int packetCount = 0;
struct sockaddr_in6 clientAddress;
char buffer[BUFFER_SIZE];
socklen_t clientAddressLength = sizeof(clientAddress);
displayConfig();
while (1)
{
// clear the buffer
memset(buffer, 0, BUFFER_SIZE);
// Receive packet
ssize_t bytesRead = recvfrom(sockFD, buffer, BUFFER_SIZE, 0, (struct sockaddr *)&clientAddress, &clientAddressLength);
if (bytesRead < 0)
{
print_error("recvfrom failed");
break;
}
packetCount++;
printf("Total packet count %d\n", packetCount);
}
}
int main()
{
int status = udp_balancer();
if(status == EXIT_FAILURE){
printf("Balancer failed to run\n");
}
else
capture();
return 0;
}
I tried running with above bpf program but it is throwing error that not able to attch bpf.
TL;DR. You cannot read from memory unless you first write to it. cBPF also doesn't offer a way to persist data across calls. That was one of the main additions of eBPF.
From the cBPF documentation:
Therefore, your program is rejected because you are trying to read from memory as the very first instruction.
In any case, there is no way to persist data with cBPF. If you want to persist data across program executions, then you will have to use eBPF and its maps.