[CentOS] forwarding packets to service in same host without using loopback network
m.roth at 5-cent.us
m.roth at 5-cent.us
Wed Apr 25 19:18:13 UTC 2012
Arif Hossain wrote:
> This question is not about linux usage. But still i think user list
> is a good crowd for linux programmer. So here it goes.
> I have this libnetfilter_queue application which receives packets from
> kernel based on some iptables rule. Before going straight to my
> problem, i'm giving a sample workable code and other tools to set up a
> test environment so that We problem definition and possible solutions
> can be more accurate and robust.
>
> The following code describes the core functionality of the application:
>
Are you a student?
I ask, because in both functions, the first thing I see is
> if (ph)
> {
> id = ntohl (ph->packet_id);
> printf ("hw_protocol=0x%04x hook=%u id=%u ",
> ntohs (ph->hw_protocol), ph->hook, id);
> }
and you have *no* error handling if !ph. If this should never be true,
then a) why is it in an if statement, and b) I will say, with 99.44%
confidence, that it *will* be the case sometime, in a situation you
haven't thought of.
I'm at work, so I may get back to this after work.
mark
>
> #include <stdio.h>
> #include <stdlib.h>
> #include <unistd.h>
> #include <netinet/in.h>
> #include <linux/types.h>
> #include <linux/netfilter.h> /* for NF_ACCEPT */
> #include <errno.h>
>
> #include <libnetfilter_queue/libnetfilter_queue.h>
> #define PREROUTING 0
> #define POSTROUTING 4
> #define OUTPUT 3
>
>
> /* returns packet id */
> static u_int32_t
> print_pkt (struct nfq_data *tb)
> {
> int id = 0;
> struct nfqnl_msg_packet_hdr *ph;
> struct nfqnl_msg_packet_hw *hwph;
> u_int32_t mark, ifi;
> int ret;
> unsigned char *data;
>
> ph = nfq_get_msg_packet_hdr (tb);
> if (ph)
> {
> id = ntohl (ph->packet_id);
> printf ("hw_protocol=0x%04x hook=%u id=%u ",
> ntohs (ph->hw_protocol), ph->hook, id);
> }
>
> hwph = nfq_get_packet_hw (tb);
> if (hwph)
> {
> int i, hlen = ntohs (hwph->hw_addrlen);
>
> printf ("hw_src_addr=");
> for (i = 0; i < hlen - 1; i++)
> printf ("%02x:", hwph->hw_addr[i]);
> printf ("%02x ", hwph->hw_addr[hlen - 1]);
> }
>
> mark = nfq_get_nfmark (tb);
> if (mark)
> printf ("mark=%u ", mark);
>
> ifi = nfq_get_indev (tb);
> if (ifi)
> printf ("indev=%u ", ifi);
>
> ifi = nfq_get_outdev (tb);
> if (ifi)
> printf ("outdev=%u ", ifi);
> ifi = nfq_get_physindev (tb);
> if (ifi)
> printf ("physindev=%u ", ifi);
>
> ifi = nfq_get_physoutdev (tb);
> if (ifi)
> printf ("physoutdev=%u ", ifi);
>
> ret = nfq_get_payload (tb, &data);
> if (ret >= 0)
> printf ("payload_len=%d ", ret);
>
> fputc ('\n', stdout);
>
> return id;
> }
>
>
> static int
> cb (struct nfq_q_handle *qh, struct nfgenmsg *nfmsg,
> struct nfq_data *nfa, void *data)
> {
> uint32_t ip_src, ip_dst;
> struct in_addr s_ip;
> struct in_addr d_ip;
> uint16_t src_port;
> uint16_t dst_port;
> int verdict;
> int id;
> int ret;
> unsigned char *buffer;
> struct nfqnl_msg_packet_hdr *ph = nfq_get_msg_packet_hdr (nfa);
> if (ph)
> {
> id = ntohl (ph->packet_id);
> printf ("received packet with id %d", id);
> }
> ret = nfq_get_payload (nfa, &buffer);
> ip_src = *((uint32_t *) (buffer + 12));
> ip_dst = *((uint32_t *) (buffer + 16));
> src_port = *((uint16_t *) (buffer + 20));
> dst_port = *((uint16_t *) (buffer + 22));
> s_ip.s_addr = (uint32_t) ip_src;
> d_ip.s_addr = (uint32_t) ip_dst;
> *(buffer + 26) = 0x00;
> *(buffer + 27) = 0x00;
> printf ( "source IP %s", inet_ntoa (s_ip));
> printf ( "destination IP %s", inet_ntoa (d_ip));
> printf ( "source port %d", src_port);
> printf ( "destination port %d", dst_port);
> if (ret)
> {
> switch (ph->hook)
> {
> case PREROUTING:
> printf ( "inbound packet");
> //my_mangling_fun();
> break;
> case OUTPUT:
> printf ( "outbound packet");
> //my_mangling_fun();
> break;
> }
> }
> verdict = nfq_set_verdict (qh, id, NF_ACCEPT, ret, buffer);
> if (verdict)
> printf ( "verdict ok");
> return verdict;
> }
>
> int
> main (int argc, char **argv)
> {
> struct nfq_handle *h;
> struct nfq_q_handle *qh;
> struct nfnl_handle *nh;
> int fd;
> int rv;
> char buf[4096] __attribute__ ((aligned));
>
> printf ("opening library handle\n");
> h = nfq_open ();
> if (!h)
> {
> fprintf (stderr, "error during nfq_open()\n");
> exit (1);
> }
>
> printf ("unbinding existing nf_queue handler for AF_INET (if
> any)\n");
> if (nfq_unbind_pf (h, AF_INET) < 0)
> {
> fprintf (stderr, "error during nfq_unbind_pf()\n");
> exit (1);
> }
>
> printf ("binding nfnetlink_queue as nf_queue handler for
> AF_INET\n");
> if (nfq_bind_pf (h, AF_INET) < 0)
> {
> fprintf (stderr, "error during nfq_bind_pf()\n");
> exit (1);
> }
>
> printf ("binding this socket to queue '0'\n");
> qh = nfq_create_queue (h, 0, &cb, NULL);
> if (!qh)
> {
> fprintf (stderr, "error during nfq_create_queue()\n");
> exit (1);
> }
>
> printf ("setting copy_packet mode\n");
> if (nfq_set_mode (qh, NFQNL_COPY_PACKET, 0xffff) < 0)
> {
> fprintf (stderr, "can't set packet_copy mode\n");
> exit (1);
> }
>
> fd = nfq_fd (h);
>
> for (;;)
> {
> if ((rv = recv (fd, buf, sizeof (buf), 0)) >= 0)
> {
> printf ("pkt received\n");
> nfq_handle_packet (h, buf, rv);
> continue;
> }
> /* if your application is too slow to digest the packets that
> * are sent from kernel-space, the socket buffer that we use
> * to enqueue packets may fill up returning ENOBUFS. Depending
> * on your application, this error may be ignored. Please, see
> * the doxygen documentation of this library on how to improve
> * this situation.
> */
> if (rv < 0 && errno == ENOBUFS)
> {
> printf ("losing packets!\n");
> continue;
> }
> perror ("recv failed");
> break;
> }
>
> printf ("unbinding from queue 0\n");
> nfq_destroy_queue (qh);
>
> #ifdef INSANE
> /* normally, applications SHOULD NOT issue this command, since
> * it detaches other programs/sockets from AF_INET, too ! */
> printf ("unbinding from AF_INET\n");
> nfq_unbind_pf (h, AF_INET);
> #endif
>
> printf ("closing library handle\n");
> nfq_close (h);
>
> exit (0);
> }
>
>
> Notice in the callback function two calls to my_mangling_fun() is
> commented out. This is where i mangle the incoming and outgoing
> packet. I think this code would be sufficient to describe my case. If
> further clarification is need please ask, i will post further details.
>
> Lets say accompanying iptables rules are following :
>
> $iptables -t mangle -A PREROUTING -p udp --dport 5000 -j NFQUEUE
> $iptables -t mangle -A OUTPUT -p udp --sport 5000 -j NFQUEUE
>
> lets compile and fire udp the thing.
>
> $gcc -g3 nfq_test.c -lnfnetlink -lnetfilter_queue
> $./a.out (should be as root)
>
> now we can feed garbage udp payload to this thing by netcat both
> client and server mode
>
> $nc -ul 5000
> $nc -uvv <IP> 5000
>
> This will print the packet from my netfilter_queue app in stdout. Now
> that the development environment is set up, we can move to the next
> thing.
>
> What we are trying to achieve is following :
>
> Our server is listening on 5000 port. Now all incoming packet destined
> to udp port 5000 will be queued by kernel. And the handle to this
> queue will be given to user application we listed earlier. This queue
> mechanism works like this: When a packet is available, the callback
> function(cb() in our code) is called. after processing, the callback
> function calls nfq_set_verdict(). after a **verdict** is returned,
> next packet will pop from the queue. notice that a packet will not pop
> from queue if its preceding packet has not been issued a verdict. This
> verdict values are NF_ACCEPT for accepting packet, NF_DROP for
> dropping the packet.
>
> Now what if i want to concatenate the udp payloads of the incoming and
> outgoing packet without touching client and server side code?
>
> If i want to concatenate udp payloads from our app this very app, then
> we need to have multiple packets at hand. But we have seen that a
> packet does not pops from queue before a verdict is issued to its
> preceding one.
>
> So how can this be done?
>
> One possible solution is issue a NF_DROP to every packet and save
> those packets in an intermediate data structure. Let's say we have
> done it. But how can this packet can be delivered to the service
> listening on 5000 port?
>
> We can't use network stack for delivering the packet, because if we
> do, then packets will end up in NFQUEUE again.
>
> Another problem is, the server is totally agnostic about this app.
> That means it should not see any difference in the packets. It should
> see packets as if it came from the original client.
>
> I have heard that a application can send data to a server in the same
> host without using network layer(ip,port) by writing some files. I do
> not know the validity of this statement. But if anyone knows anything
> about it , it will be wonderful.
>
> Another possible solution proposed to me is :
>
> 1.store packets in the application and return verdict NF_DROP
> 2.re-inject packets into the network stack using RAW sockets
> 3.tag concatenated UDP packets with a DSCP (see IP packet format)
> 4.in iptables, add a rule to match on this DSCP (--dscp) and ACCEPT
> the packet directly, without it passing through your netfilter
> application
> 5.If the provider already tags some packets with DSCP, you can add
> some iptables rules to clear them, like:
>
> $iptables -t mangle -A INPUT -j DSCP --set-dscp 0
>
>
>
>
> --
> -aft
> _______________________________________________
> CentOS mailing list
> CentOS at centos.org
> http://lists.centos.org/mailman/listinfo/centos
>
More information about the CentOS
mailing list