Reference

flexible struct arrary as a memeber of another struct

Is using flexible array memebers in C bad practice?

My summary on Flexible array

What is a flexible array?

1
2
3
4
5
6
7
8
9
10
11
/**
* struct ethtool_sfeatures - command to request change in device's features
* @cmd: command number = %ETHTOOL_SFEATURES
* @size: array size of the features[] array
* @features: feature change masks
*/
struct ethtool_sfeatures {
__u32 cmd;
__u32 size;
struct ethtool_set_features_block features[];
};

struct ethtool_set_features_block features[] is a flexible array.

is NOT a pointer, it is an in place array which is contiguous with the struct.

How to init a flexible array?

1
2
3
4
5
6
7
8
9
struct Header {
size_t d;
long v[];
};
typedef struct Header Header;
size_t n = 123; // can dynamically change during program execution
// ...
Header *h = malloc(sizeof(Header) + sizeof(long[n]));
h->n = n;

Or:

1
Header *h = malloc(sizeof *h + n * sizeof h->v[0]);

Note that sizeof(Header) includes eventual padding bytes, thus, the following allocation is incorrect and may yield a buffer overflow:

1
Header *h = malloc(sizeof(size_t) + sizeof(long[n])); // invalid!

My example code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
#include <errno.h>
#include <linux/ethtool.h>
#include <linux/if.h>
#include <linux/sockios.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <unistd.h>

#define ETHTOOL_DEV_FEATURE_WORDS 2

static void ethnl_features_to_bitmap32(__u32 *dest, __u64 src) {
unsigned int i;

for (i = 0; i < ETHTOOL_DEV_FEATURE_WORDS; i++) {
dest[i] = src >> (32 * i);
}
}

static int init_struct() {
int sockfd;
struct ifreq ifr;
// struct ethtool_sfeatures cmd;
struct ethtool_sfeatures *cmd =
malloc(sizeof *cmd + ETHTOOL_DEV_FEATURE_WORDS * sizeof cmd->features[0]);

if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
printf("socket error\n");
return -1;
}
strcpy(ifr.ifr_name, "net0");
printf("printf ifr_name: %s \n", ifr.ifr_name);
cmd->cmd = ETHTOOL_SFEATURES;
cmd->size = ETHTOOL_DEV_FEATURE_WORDS;
__u32 valid[ETHTOOL_DEV_FEATURE_WORDS];
__u64 features = (1UL << 9);
ethnl_features_to_bitmap32(valid, features);
for (int i = 0; i < ETHTOOL_DEV_FEATURE_WORDS; i++) {
cmd->features[i].valid = valid[i];
cmd->features[i].requested = 0;
}
printf("printf ifr_name: %s \n", ifr.ifr_name);
ifr.ifr_data = (char *)cmd;
printf("printf ifr_name: %s \n", ifr.ifr_name);
if (ioctl(sockfd, SIOCETHTOOL, &ifr) < 0) {
printf("ioctl error: %d\n", errno);
close(sockfd);
free(cmd);
return -1;
}
close(sockfd);
free(cmd);
return -1;
}

static int get_features() {
int sockfd;
struct ifreq ifr;
struct ethtool_gfeatures *cmd =
malloc(sizeof *cmd + ETHTOOL_DEV_FEATURE_WORDS * sizeof cmd->features[0]);

if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
printf("socket error\n");
return -1;
}
strcpy(ifr.ifr_name, "enp2s0");

cmd->cmd = ETHTOOL_GFEATURES;
cmd->size = ETHTOOL_DEV_FEATURE_WORDS;
ifr.ifr_data = (char *)cmd;

if (ioctl(sockfd, SIOCETHTOOL, &ifr) < 0) {
printf("ioctl error: %d\n", errno);
close(sockfd);
free(cmd);
return -1;
}
close(sockfd);
free(cmd);
return -1;
}
int main(int argc, char *argv[]) {
init_struct();
// get_features();
return EXIT_SUCCESS;
}