Converting audio formats with libav in c on linux - audio

I am trying to write an app, where part of it converts an audio file to a different format. But the output is garbage (well, that's what it looks like in audacity).
After trying many different things, I ended up with this: a raw audio file that I try to convert and write out as an MP3 file. I know the raw audio is OK (via audacity). But the MP3 again is garbage.
Here is the code I am using (I pieced this together from the libav examples):
/*
* flac2mp3.cpp
*
* Created on: Aug 20, 2014
* Author: ken
*/
#include <stddef.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/ioctl.h>
#include <string.h>
#include <pthread.h>
#include <signal.h>
#include <exception> // std::exception
#include <cerrno>
#include <assert.h>
#include <time.h>
#include <fcntl.h>
#include <linux/types.h>
#ifndef __STDC_FORMAT_MACROS
#define __STDC_FORMAT_MACROS
#include <inttypes.h>
#endif
#ifndef INT64_C
#define INT64_C(c) (c ## LL)
#define UINT64_C(c) (c ## ULL)
#endif
extern "C"
{
#include <libavutil/avassert.h>
#include <libavutil/channel_layout.h>
#include <libavutil/opt.h>
#include <libavutil/mathematics.h>
#include <libavutil/timestamp.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
#include <libswresample/swresample.h>
#ifdef CPP_W_LITERAL_SUFFIX
#undef CPP_W_LITERAL_SUFFIX
#include <libavutil/timestamp.h>
#define CPP_W_LITERAL_SUFFIX
#endif
#ifdef av_err2str
#undef av_err2str
#endif
#define av_err2str(errnum) \
av_make_error_string((char*)(__builtin_alloca(AV_ERROR_MAX_STRING_SIZE)),\
AV_ERROR_MAX_STRING_SIZE, errnum)
#ifdef av_ts2timestr
#undef av_ts2timestr
#endif
#define av_ts2timestr(ts, tb) \
av_ts_make_time_string((char*)(__builtin_alloca(AV_TS_MAX_STRING_SIZE)), ts, tb)
}
typedef unsigned char BYTE;
#include <algorithm>
#include <fcntl.h>
static AVFormatContext *encfmt_ctx = NULL;
static AVCodecContext *audio_enc_ctx;
static AVStream *audio_encstream = NULL;
static int audio_frame_deccount = 0, audio_frame_enccount = 0;
static SwrContext *swr;
static BYTE **outpb;
static const BYTE **inpb;
#define MP3_SAMPLE_FMT AV_SAMPLE_FMT_S16P
#define MP3_BITRATE 256000
#define MP3_SAMPLE_RATE 44100
#define FLAC_SAMPLE_FMT AV_SAMPLE_FMT_S16
#define FLAC_BITRATE 256000
#define FLAC_SAMPLE_RATE 44100
#define SAMPLES_TO_CB(stc, cs) (av_get_bytes_per_sample(stc->codec->sample_fmt) * stc->codec->channels * cs)
#define SAMPLES_FROM_CB(stc, cb) cb/(av_get_bytes_per_sample(stc->codec->sample_fmt) * stc->codec->channels)
#define BYTES_TO_READ 2048
bool encode_buffer(BYTE* buff, int cbdec, bool flush)
{
int ret;
int got_frame;
int line_size;
AVPacket epkt = {0};
AVFrame *eframe = avcodec_alloc_frame();
av_init_packet(&epkt);
/*
* convert audio
*/
long int c_insamples = cbdec/(4);
// long int delay = swr_get_delay(swr,(long int) 44100);
long int delay = 0;
int c_outsamples = av_rescale_rnd(delay + c_insamples,
(long int) MP3_SAMPLE_RATE,
(long int) FLAC_SAMPLE_RATE, AV_ROUND_UP);
ret = av_samples_alloc(outpb, &line_size, 2,
c_outsamples,MP3_SAMPLE_FMT, 0);
av_assert0(ret >= 0);
inpb[0] = buff;
ret = swr_convert(swr, outpb, c_outsamples, inpb, c_insamples);
av_assert0(ret >= 0);
/*
* encode
*/
audio_frame_deccount +=(cbdec/4);
printf("cbdec=%d, deccount=%d\n", cbdec, audio_frame_deccount);
eframe->nb_samples = cbdec/(4);
eframe->pts = (audio_frame_deccount * ( av_rescale_q(1, audio_encstream->codec->time_base, audio_encstream->time_base)));
avcodec_fill_audio_frame(eframe, 2, MP3_SAMPLE_FMT, outpb[0], cbdec, 1);
epkt.stream_index = audio_encstream->index;
eframe->nb_samples = audio_enc_ctx->frame_size;
ret = avcodec_encode_audio2(audio_enc_ctx, &epkt, eframe, &got_frame);
av_assert0(ret >= 0);
if (!got_frame)
{
// av_free(outpb[0]);
return false;
}
epkt.stream_index = audio_encstream->index;
audio_frame_enccount ++ ;
/* Write the compressed frame to the media file. */
ret = av_interleaved_write_frame(encfmt_ctx, &epkt);
av_free(outpb[0]);
avcodec_free_frame(&eframe);
return true;
}
int open_encodec_context(AVFormatContext *fmt_ctx, enum AVMediaType type)
{
int ret;
AVStream *st;
AVCodec *enc = NULL;
enc = avcodec_find_encoder(fmt_ctx->oformat->audio_codec);
av_assert0(enc != NULL);
st = avformat_new_stream(fmt_ctx, enc);
/* find encoder for the stream */
st->codec->sample_fmt = MP3_SAMPLE_FMT;
st->codec->bit_rate = MP3_BITRATE;
st->codec->sample_rate = MP3_SAMPLE_RATE;
st->codec->channels = 2;
ret = avcodec_open2(st->codec, enc, NULL);
av_assert0(ret >= 0);
return 1;
}
int main(int argc, char **argv)
{
const char *fn_out = argv[1];
int ret;
av_register_all();
ret = avformat_alloc_output_context2(&encfmt_ctx, NULL, NULL, fn_out);
av_assert0(ret >= 0);
if (open_encodec_context(encfmt_ctx, AVMEDIA_TYPE_AUDIO) >= 0)
{
audio_encstream = encfmt_ctx->streams[0];
audio_enc_ctx = audio_encstream->codec;
av_dump_format(encfmt_ctx, 0, fn_out, 1);
}
BYTE pbo[sizeof(BYTE*) * av_sample_fmt_is_planar(MP3_SAMPLE_FMT) ? 2 : 1];
outpb = (BYTE**) pbo;
outpb[0] = NULL;
BYTE pbi[sizeof(BYTE*) * 2];
inpb = (const BYTE**) pbi;
inpb[0] = NULL;
swr = swr_alloc();
av_assert0(swr);
/* set options */
av_opt_set_int(swr, "in_channel_layout", 2, 0);
av_opt_set_int(swr, "in_channel_count", 2, 0);
av_opt_set_int(swr, "in_sample_rate", FLAC_SAMPLE_RATE,0);
av_opt_set_sample_fmt(swr, "in_sample_fmt", FLAC_SAMPLE_FMT, 0);
av_opt_set_int(swr, "out_channel_layout",2, 0);
av_opt_set_int(swr, "out_sample_rate", MP3_SAMPLE_RATE, 0);
av_opt_set_sample_fmt(swr, "out_sample_fmt",MP3_SAMPLE_FMT, 0);
ret = swr_init(swr);
av_assert0(ret >= 0);
if (!(encfmt_ctx->oformat->flags & AVFMT_NOFILE))
{
ret = avio_open(&encfmt_ctx->pb, fn_out, AVIO_FLAG_WRITE);
if (ret < 0)
{
fprintf(stderr, "Could not open '%s': %s\n", fn_out,
av_err2str(ret));
return 1;
}
}
ret = avformat_write_header(encfmt_ctx, NULL);
av_assert0(ret >= 0);
int cbread = 0;
int fd = open("/nas/temp/flac.raw", O_RDONLY);
assert(fd > 0);
int cb2read = BYTES_TO_READ;
BYTE b[cb2read];
int c_reads = 0;
while ((cbread = read(fd, b, cb2read)) > 0)
{
c_reads++;
encode_buffer(b, cbread, false);
}
close(fd);
ret = av_interleaved_write_frame(encfmt_ctx, NULL);
av_assert0(ret >= 0);
av_write_trailer(encfmt_ctx);
avio_close(encfmt_ctx->pb);
avcodec_close(audio_enc_ctx);
avformat_free_context(encfmt_ctx);
exit(1);
}
The input data is from a decoded flac file (ie, the raw audio data from it). It is, according to libav/ffmpeg: format=s16, sample rate=44100, channels=2.
Any ideas?
TIA
ken

Related

How does named pipe in Linux works?

I'm trying to use named pipe in Linux, using mkfifo.
Below are codes for sender / reader of named pipe :
#include <fcntl.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#define MSG_SIZE 100
//SENDER
int main() {
char msg[MSG_SIZE] = "FIFO is for first-in, first-out.";
int fd;
int cnt;
if(fd = open("./hello", O_RDWR) < 0) { //fifo acts as a typical file
perror("Failed to make fifo : ");
printf("Error : %d\n", errno);
exit(1);
}
int len = strlen(msg) + 1;
while(1) {
if(write(fd, msg, len) == -1) {
perror("Failed to write into fifo : ");
exit(1);
}
else{
printf("Succeed to write msg!");
break;
}
}
sleep(1);
return 0;
}
#include <fcntl.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#define MSG_SIZE 100
//READER
int main() {
char msg_rc[MSG_SIZE];
int fd;
if(access("./hello",F_OK) == 0) {
fd = open("./hello", O_RDWR);
if(fd < 0) {
perror("Failed to open fifo : ");
exit(1);
}
}
else {
if(fd = mkfifo("./hello", O_RDWR) < 0) {
perror("Failed to open fifo : ");
exit(1);
}
}
printf("Waiting for fifo update");
while(1) {
if(read(fd, msg_rc, MSG_SIZE) == -1) {
perror("Failed to read from fifo : ");
exit(1);
}
printf("Read msg : %s\n", msg_rc);
break;
}
return 0;
}
When I used these codes,
% ./fifoSender
FIFO is for first-in, first-out.Succeed to write msg!%
% ./fifoReader
fifoSender shows msg, and fifoReader shows no msg, which are not desirable behavior.
Any advice will be helpful. Thanks in advance
The main problem is these two lines
if(fd = open("./hello", O_RDWR) < 0) {
if(fd = mkfifo("./hello", O_RDWR) < 0) {
They don't do what you think they do.
Firstly, O_RDWR is a wrong argument for mkfifo. It expects Unix file permissions mask, such as S_IRWU"S_IRWG|S_IRWO, or just 0666.
Secondly and most importantly, since the priority of = is lower than that of <, they are parsed like this:
if(fd = (open("./hello", O_RDWR) < 0)) {
Now, when open("./hello", O_RDWR) is less than 0 (which is highly likely, given that it is either never created or created with an incorrect mode), fd becomes 1, which is the standard output file descriptor.
The correct incantation is
if((fd = open("./hello", O_RDWR)) < 0) { // note () around assignment
if((fd = mkfifo("./hello", 0666)) < 0) {
There are several more subtle shortcomings.
Here is the working code,
// sender
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#define PIPE_NAME "./hello"
int main(int argc, char *argv[])
{
const char *message = "FIFO is for first-in, first-out\n";
// Open the pipe for writing
int pipe_fd = open(PIPE_NAME, O_WRONLY);
if (pipe_fd < 0) {
perror("open");
exit(1);
}
// Write data to the pipe
while(1){
int n = write(pipe_fd, message, strlen(message));
if (n < 0) {
perror("write");
exit(1);
}
sleep(1);
}
// Close the pipe
close(pipe_fd);
return 0;
}
//receiver
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#define PIPE_NAME "./hello"
int main(int argc, char *argv[])
{
// Create the named pipe
mknod(PIPE_NAME, S_IFIFO | 0666, 0);
// Open the pipe for reading
int pipe_fd = open(PIPE_NAME, O_RDONLY);
if (pipe_fd < 0) {
perror("open");
exit(1);
}
// Read data from the pipe
char buffer[1024];
while(1){
int n = read(pipe_fd, buffer, sizeof(buffer));
if (n < 0) {
perror("read");
exit(1);
}
// Print the data that was read
printf("Received: %.*s\n", n, buffer);
}
// Close the pipe
close(pipe_fd);
return 0;
}

Why this kernel module does not handle open and read syscall?

I'm learning about Linux kernel module with Ubuntu 20.04 (Linux kernel 5.4.0-37 generic). The following is the code that actual code.
I have expected to following module to that pass single byte random number between 0-255 (get_random_bytes(&c, 1)) to user space buffer when it handle ->read() syscall then print a message to dmesg.
But Unfortunately, for now, it does not work. It seems like does not add handle of ->read() and ->open() syscalls.
Why it does not handle ->read() and ->open() syscall?
User space applicattion code (open and read device file) app.c:
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>
int main() {
char c[8];
memset(c, '\0', 8);
int fd = open("/dev/devsaikoro0", O_RDONLY);
read(fd, &c, 1);
//printf("Hello\n");
printf("%s\n", c);
}
Kernel module code:
#include <linux/init.h>
#include <linux/types.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <asm/uaccess.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/uaccess.h>
#include <linux/random.h>
MODULE_LICENSE("Dual BSD/GPL");
static int devsaikoro_num = 3;
static int devsaikoro_major = 0;
static int devsaikoro_minor = 0;
static struct cdev devsaikoro_cdev;
static struct class *devsaikoro_class = NULL;
static dev_t saikoro_dev;
ssize_t read_num(struct file * filp, char __user *buf, size_t count, loff_t *f_pos)
{
int retval;
char c;
get_random_bytes(&c, 1);
if (copy_to_user(buf, &c, 1)) {
printk("devsaikoro read failed\n");
retval = -EFAULT;
return retval;
} else {
printk("devsaikoro read succeeded\n");
return 1;
}
}
struct file_operations fops = {
.read = read_num,
};
int saikoro_open(struct inode *inode, struct file *file) {
printk("devsaikoro open\n");
file->f_op = &fops;
return 0;
}
struct file_operations fops2 = {
.open = saikoro_open,
};
static int devsaikoro_init(void)
{
dev_t dev = MKDEV(devsaikoro_major, 0);
int alloc_ret = 0;
int major;
int cdev_err = 0;
struct device *class_dev = NULL;
alloc_ret = alloc_chrdev_region(&dev, 0, devsaikoro_num, "devsaikoro");
if (alloc_ret) {
goto error;
}
devsaikoro_major = major = MAJOR(dev);
cdev_init(&devsaikoro_cdev, &fops2);
devsaikoro_cdev.owner = THIS_MODULE;
cdev_err = cdev_add(&devsaikoro_cdev, MKDEV(devsaikoro_major, 0), devsaikoro_num);
if (cdev_err)
goto error;
devsaikoro_class = class_create(THIS_MODULE, "devsaikoro");
if (IS_ERR(devsaikoro_class))
goto error;
saikoro_dev = MKDEV(devsaikoro_major, devsaikoro_minor);
class_dev = device_create(devsaikoro_class, NULL, saikoro_dev, NULL, "devsaikoro%d", devsaikoro_minor);
printk(KERN_ALERT "devsaikoro_driver (major %d) installed\n", major);
return 0;
error:
if (cdev_err == 0) {
cdev_del(&devsaikoro_cdev);
}
if (alloc_ret == 0) {
unregister_chrdev_region(dev, devsaikoro_num);
}
return -1;
}
static void devsaikoro_exit(void)
{
dev_t dev = MKDEV(devsaikoro_major, 0);
device_destroy(devsaikoro_class, saikoro_dev);
class_destroy(devsaikoro_class);
cdev_del(&devsaikoro_cdev);
unregister_chrdev_region(dev, devsaikoro_num);
printk(KERN_ALERT "devsaikoro driver removed\n");
}
module_init(devsaikoro_init);
module_exit(devsaikoro_exit);

Example code on using PACKET_QDISC_BYPASS option on ethernet raw socket on linux

According to http://man7.org/linux/man-pages/man7/packet.7.html, there is a new option
PACKET_QDISC_BYPASS since linux 3.14. This has the potential of sending and receiving packets a lot faster.
Saw a sample code: https://github.com/netoptimizer/network-testing/blob/master/src/raw_packet_send.c, but unfortunately it doesn't have the code on sending packets.
I added some code to send packets but it has a problem with sendto.
# ./raw_packet_send p6p1 64
Lame RAW/PF_PACKET socket TX test program
Enabled kernel qdisc bypass
error sendto : Invalid argument
Not sure why. Here is the code (original raw_packet_send.c with my silly code). Please let me know how could make it work or point me to some good simple sample code. Thanks.
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <unistd.h>
#include <linux/if_packet.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <netinet/in.h>
//#include <stdio.h>
//#include <sys/types.h>
//#include <sys/stat.h>
//#include <sys/socket.h>
//#include <sys/mman.h>
//#include <linux/filter.h>
//#include <ctype.h>
//#include <fcntl.h>
//#include <unistd.h>
//#include <bits/wordsize.h>
//#include <net/ethernet.h>
//#include <netinet/ip.h>
//#include <arpa/inet.h>
//#include <stdint.h>
//#include <string.h>
//#include <assert.h>
//#include <net/if.h>
//#include <inttypes.h>
//#include <poll.h>
//#include <unistd.h>
#ifndef PACKET_QDISC_BYPASS
#define PACKET_QDISC_BYPASS 20
#endif
#include "common_socket.h"
char pkt[2000] = {0x00, 1,2,3,4,0, 0,1,2,3,4,1, 8, 0};;
int len = 96;
char intfName[100] = "em1";
/* Avail in kernel >= 3.14
* in commit d346a3fae3 (packet: introduce PACKET_QDISC_BYPASS socket option)
*/
void set_sock_qdisc_bypass(int fd, int verbose)
{
int ret, val = 1;
ret = setsockopt(fd, SOL_PACKET, PACKET_QDISC_BYPASS, &val, sizeof(val));
if (ret < 0) {
printf("[DEBUG] %s(): err:%d errno:%d\n", __func__, ret, errno);
if (errno == ENOPROTOOPT) {
if (verbose)
printf("No kernel support for PACKET_QDISC_BYPASS"
" (kernel < 3.14?)\n");
} else {
perror("Cannot set PACKET_QDISC_BYPASS");
}
} else
if (verbose) printf("Enabled kernel qdisc bypass\n");
}
int pf_tx_socket(int ver)
{
int ret, val = 1;
/* Don't use proto htons(ETH_P_ALL) as we only want to transmit */
int sock = socket(PF_PACKET, SOCK_RAW, 0);
//int sock = socket(AF_INET,SOCK_PACKET,htons(3));
if (sock == -1) {
perror("Creation of RAW PF_SOCKET failed!\n");
exit(1);
}
ret = Setsockopt(sock, SOL_PACKET, PACKET_VERSION, &ver, sizeof(ver));
return sock;
}
void mybind(int sock, char *intf) {
struct ifreq ifr;
int rc;
memset((char*)&ifr, 0, sizeof(ifr));
snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), intf);
if ((rc = setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, (void *)&ifr, sizeof(ifr))) < 0)
{
perror("Server-setsockopt() error for SO_BINDTODEVICE");
printf("%s\n", strerror(errno));
close(sock);
exit(-1);
}
}
int flood (int sock) {
struct sockaddr intfAddrs;
char cmd[100];
int tmp;
memset((char*)&intfAddrs, 0, sizeof (struct sockaddr));
intfAddrs.sa_family = PF_PACKET;
strcpy((char*)(intfAddrs.sa_data), intfName);
sprintf(cmd, "ifconfig %s promisc", intfName); system(cmd);
while (1) {
while (1) {
tmp = sendto(sock, pkt, len, 0, &intfAddrs, sizeof(intfAddrs));
if (tmp != len) {perror("error sendto "); exit(0); }
}
}
}
int main(int argc, char **argv)
{
if (argc > 1) { strcpy(intfName, argv[1]); }
if (argc > 2) { len = atoi(argv[2]); }
printf("Lame RAW/PF_PACKET socket TX test program\n");
int sock = pf_tx_socket(0);
set_sock_qdisc_bypass(sock, 1);
mybind(sock, intfName);
flood(sock);
return 0;
}
I think you need to change from PF_PACKET to AF_PACKET (although it looks like PF_PACKET is an alias to AF_PACKET) but I don't have any success when using PF_PACKET.
This code works for me:
// Create a raw socket
sock_fd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
if (sock_fd == -1) {
perror("Can't create AF_PACKET socket");
return EXIT_FAILURE;
}
// Check kernel version
static const int32_t sock_qdisc_bypass = 1;
int32_t sock_qdisc_ret = setsockopt(thd_opt->sock_fd, SOL_PACKET, PACKET_QDISC_BYPASS, &sock_qdisc_bypass, sizeof(sock_qdisc_bypass));
if (sock_qdisc_ret == -1) {
perror("Can't enable QDISC bypass on socket");
return EXIT_FAILURE;
}
This is from setup_socket_mmap() here

Net Link Linux User code bind socket call always fail for multicast group Id (non zero value)

Hi am trying to implement net link user code and kernel code every thing works fine for unicast (src_addr.nl_groups = 0;). For mulicast, user code bind call always fails for non zero src_addr.nl_groups value. Really am not sure what value to put for multicast and how to proceed further. I checked the usage of netlink_broadcast in kernel source tree, so I put the same group Id value (RTMGRP_LINK) here. For unicast I found good number of help in internet but for multicast I don't think so . So Please help me to proceed further.
Error am getting is:
bind: No such file or directory
./a.out: can't bind socket (3)and err : -1: No such file or directory
#include <sys/socket.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <stdio.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#define NETLINK_TEST 28
#define GROUP_IB 1
#define MAX_PAYLOAD 1024
struct sockaddr_nl src_addr, dst_addr;
struct nlmsghdr *nlh = NULL;
struct msghdr msg;
struct iovec iov;
int sock_fd;
int main(int argc, char ** argv)
{
int err;
sock_fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_TEST);
if (sock_fd<0) {
char s[BUFSIZ];
sprintf( s, "%s: can't assign fd for socket", argv[0] );
perror(s);
return -1;
}
memset(&src_addr, 0, sizeof(src_addr));
src_addr.nl_family = AF_NETLINK;
src_addr.nl_pid = getpid();
src_addr.nl_groups = 0; // Unicast
//src_addr.nl_groups = RTMGRP_LINK; /* Multicast, bind call always fails for non zero values */
err = bind(sock_fd, (struct sockaddr*)&src_addr, sizeof(src_addr));
perror("bind");
if (err<0) {
char s[BUFSIZ];
sprintf( s, "%s: can't bind socket (%d)and err : %d", argv[0], sock_fd,err );
perror(s);
return -1;
}
memset(&dst_addr, 0, sizeof(dst_addr));
nlh = (struct nlhmsghdr *) malloc(NLMSG_SPACE(MAX_PAYLOAD));
memset(nlh, 0, NLMSG_SPACE(MAX_PAYLOAD));
iov.iov_base = (void *)nlh;
iov.iov_len = NLMSG_SPACE(MAX_PAYLOAD);
msg.msg_name = (void *)&dst_addr;
msg.msg_namelen = sizeof(dst_addr);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
printf("pid : %d\n Waiting for messages from kernel...\n",getpid());
recvmsg(sock_fd, &msg, 0);
printf("Message : %s\n", NLMSG_DATA(nlh));
close(sock_fd);
return 0;
}
Netlink socket binds are sensitive to what USER you are- I've seen them reliably fail if you are not running the program in question as 'root', at least on RedHat 6.
Try running as root 1st, before changing your logic. If you get the same failure as you do in normal operation, then you know it isn't (necessarily) a permissions issue.
The issue is
sock_fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_TEST);
Does you kernel module define the NETLINK_TEST family? your own family might must be supported at kernel module and it should post the message in the proper group using nlmsg_multicast()
RTMGRP_LINK is group defined in NETLINK_ROUTE.
This sample code is example for multicast
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <linux/netlink.h>
#include <unistd.h>
#define MYPROTO NETLINK_USERSOCK
#define MYMGRP 21
int open_netlink(void)
{
int sock;
struct sockaddr_nl addr;
int group = MYMGRP;
sock = socket(AF_NETLINK, SOCK_RAW, MYPROTO);
if (sock < 0) {
printf("sock < 0.\n");
return sock;
}
memset((void *) &addr, 0, sizeof(addr));
addr.nl_family = AF_NETLINK;
addr.nl_pid = getpid();
/* addr.nl_groups = MYMGRP; */
if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
printf("bind < 0.\n");
return -1;
}
if (setsockopt(sock, 270, NETLINK_ADD_MEMBERSHIP, &group, sizeof(group)) < 0) {
printf("setsockopt < 0\n");
return -1;
}
return sock;
}
void read_event(int sock)
{
struct sockaddr_nl nladdr;
struct msghdr msg;
struct iovec iov;
char buffer[65536];
int ret;
iov.iov_base = (void *) buffer;
iov.iov_len = sizeof(buffer);
msg.msg_name = (void *) &(nladdr);
msg.msg_namelen = sizeof(nladdr);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
printf("Ok, listening.\n");
ret = recvmsg(sock, &msg, 0);
if (ret < 0)
printf("ret < 0.\n");
else
printf("Received message payload: %s\n", NLMSG_DATA((struct nlmsghdr *) &buffer));
}
int main(int argc, char *argv[])
{
int nls;
nls = open_netlink();
if (nls < 0)
return nls;
while (1)
read_event(nls);
return 0;
}
kernel module:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/netlink.h>
#include <net/netlink.h>
#include <net/net_namespace.h>
#define MYPROTO NETLINK_USERSOCK
#define MYGRP 21
static struct sock *nl_sk = NULL;
static void send_to_user(void)
{
struct sk_buff *skb;
struct nlmsghdr *nlh;
char *msg = "Hello from kernel";
int msg_size = strlen(msg) + 1;
int res;
pr_info("Creating skb.\n");
skb = nlmsg_new(NLMSG_ALIGN(msg_size + 1), GFP_KERNEL);
if (!skb) {
pr_err("Allocation failure.\n");
return;
}
nlh = nlmsg_put(skb, 0, 1, NLMSG_DONE, msg_size + 1, 0);
strcpy(nlmsg_data(nlh), msg);
pr_info("Sending skb.\n");
res = nlmsg_multicast(nl_sk, skb, 0, MYGRP, GFP_KERNEL);
if (res < 0)
pr_info("nlmsg_multicast() error: %d\n", res);
else
pr_info("Success.\n");
}
static int __init hello_init(void)
{
pr_info("Inserting hello module.\n");
nl_sk = netlink_kernel_create(&init_net, MYPROTO, NULL);
if (!nl_sk) {
pr_err("Error creating socket.\n");
return -10;
}
send_to_user();
netlink_kernel_release(nl_sk);
return 0;
}
static void __exit hello_exit(void)
{
pr_info("Exiting hello module.\n");
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");

Why does tcpdump enable this code to work?

I have a small network consisting of 2 hosts (OS X and linux) with a switch between them. When I run the following pair of programs, the receiver (linux) doesn't see any multicast packets. However, when I run
tcpdump -i eth0 -s 512 udp
on the linux box, everything starts to work. Can someone tell me why running tcpdump enables this to work?
Note that both machines have IPv6 enabled (if that matters). The host addresses in the code are the IPv4 addresses of the local interfaces.
On the OS X machine, I run:
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#define BUFLEN 256
int s;
int count;
struct in_addr local_address;
struct sockaddr_in multicast_address;
char buffer[BUFLEN];
int
main (int argc, char *argv[]) {
int port = 12345;
int retval = 0;
char *host_ip = "192.168.1.31";
char *multicast_ip = "224.0.0.251";
count = (int)time(NULL);
s = socket(AF_INET, SOCK_DGRAM, 0);
printf("s=%d\n", s);
local_address.s_addr = inet_addr(host_ip);
retval = setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, (char *)&local_address, sizeof(local_address));
printf("setsockopt=%d\n", retval);
memset((char *)&multicast_address, 0, sizeof(multicast_address));
multicast_address.sin_family = AF_INET;
multicast_address.sin_addr.s_addr = inet_addr(multicast_ip);
multicast_address.sin_port = htons(port);
while (1) {
sprintf(buffer, "Message number %d", count);
count++;
retval = sendto(s, buffer, BUFLEN, 0, (struct sockaddr*)&multicast_address, sizeof(multicast_address));
printf("sendto=%d\n", retval);
sleep(3);
}
return 0;
}
On the linux box, I run:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#define BUFLEN 256
int s;
struct ip_mreq multicast_group;
struct sockaddr_in localSock;
char buffer[BUFLEN];
int
main(int argc, char *argv[]) {
int reuse = 1;
int retval = 0;
int port = 12345;
char *host_ip = "192.168.1.131";
char *multicast_ip = "224.0.0.251";
s = socket(AF_INET, SOCK_DGRAM, 0);
printf("s = %d\n", s);
setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&reuse, sizeof(reuse));
printf("REUSEADDR = %d\n", retval);
memset((char *) &localSock, 0, sizeof(localSock));
localSock.sin_family = AF_INET;
localSock.sin_port = htons(port);
localSock.sin_addr.s_addr = INADDR_ANY;
retval = bind(s, (struct sockaddr*)&localSock, sizeof(localSock));
printf("bind = %d\n", retval);
multicast_group.imr_multiaddr.s_addr = inet_addr(multicast_ip);
multicast_group.imr_interface.s_addr = inet_addr(host_ip);
retval = setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&multicast_group, sizeof(multicast_group));
printf("ADD_MEMBERSHIP = %d\n", retval);
while (1) {
retval = read(s, buffer, BUFLEN);
printf("%d => [%s]\n", retval, buffer);
}
return 0;
}
Thanks.

Resources