FreeBSD kernel module which can change options at every layer in a connection.
9d8903cacecd3861b6cda58fe802e06cd717ab2e2925d204ecf4ee06745f5440
/*
* network kernel hackin' on a FreeBSD box....
*
* why not?
*
* yeah it's possible ... this is an example ... you can change hack_input()
* function as you wanna...
*
* We can change functions of a struct of inetsw[], we can change mbuf
* structures... we can access inpcb,inpcbinfo structures... we can change
* options of every layer in a connection...
*
* idea & code by pigpen [deadhead@sikurezza.org, pigpen@s0ftpj.org]
*
*/
/*
* uname -a
*
* FreeBSD storpio.cameretta.pig 4.0-19990705-CURRENT FreeBSD 4.0-19990705-
* CURRENT #4 ..... i386
*
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/kernel.h>
#include <sys/proc.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/sysctl.h>
#include <sys/syslog.h>
#include <sys/protosw.h>
#include <net/if.h>
#include <net/route.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/in_pcb.h>
#include <netinet/ip.h>
#include <netinet/ip_var.h>
#include <netinet/ip_icmp.h>
#include <netinet/udp.h>
#include <netinet/udp_var.h>
extern struct protosw inetsw[];
extern struct udpstat udpstat;
extern int log_in_vain;
extern struct inpcbhead udb;
extern struct sockaddr_in udp_in;
static void (*old_udp_input) __P((register struct mbuf *, int ));
static void hack_input __P((register struct mbuf *, int ));
static int s_load __P((struct module *, int, void *));
static int
s_load (struct module *module, int cmd, void *arg)
{
int s;
switch(cmd) {
case MOD_LOAD:
printf("Module loaded");
s = splnet();
old_udp_input = inetsw[1].pr_input;
inetsw[1].pr_input = hack_input;
splx(s);
break;
case MOD_UNLOAD:
s = splnet();
inetsw[1].pr_input = old_udp_input;
splx(s);
break;
}
return 0;
}
static moduledata_t s_mod_1 = {
"s_mod",
s_load,
0
};
DECLARE_MODULE(s_mod, s_mod_1, SI_SUB_PSEUDO, SI_ORDER_ANY);
static void
hack_input(m, iphlen)
register struct mbuf *m;
int iphlen;
{
register struct ip *ip;
register struct udphdr *uh;
register struct inpcb *inp;
struct mbuf *opts = 0;
int len;
struct ip save_ip;
udpstat.udps_ipackets++;
if (iphlen > sizeof (struct ip)) {
ip_stripoptions(m, (struct mbuf *)0);
iphlen = sizeof(struct ip);
}
ip = mtod(m, struct ip *);
if (m->m_len < iphlen + sizeof(struct udphdr)) {
if ((m = m_pullup(m, iphlen + sizeof(struct udphdr))) == 0) {
udpstat.udps_hdrops++;
return;
}
ip = mtod(m, struct ip *);
}
uh = (struct udphdr *)((caddr_t)ip + iphlen);
len = ntohs((u_short)uh->uh_ulen);
if (ip->ip_len != len) {
if (len > ip->ip_len || len < sizeof(struct udphdr)) {
udpstat.udps_badlen++;
goto bad;
}
m_adj(m, len - ip->ip_len);
/* ip->ip_len = len; */
}
save_ip = *ip;
if (uh->uh_sum) {
bzero(((struct ipovly *)ip)->ih_x1, 9);
((struct ipovly *)ip)->ih_len = uh->uh_ulen;
uh->uh_sum = in_cksum(m, len + sizeof (struct ip));
if (uh->uh_sum) {
udpstat.udps_badsum++;
m_freem(m);
return;
}
}
if (IN_MULTICAST(ntohl(ip->ip_dst.s_addr)) ||
in_broadcast(ip->ip_dst, m->m_pkthdr.rcvif)) {
struct inpcb *last;
udp_in.sin_port = uh->uh_sport;
udp_in.sin_addr = ip->ip_src;
m->m_len -= sizeof (struct udpiphdr);
m->m_data += sizeof (struct udpiphdr);
last = NULL;
for (inp = udb.lh_first; inp != NULL; inp = inp->inp_list.le_next) {
if (inp->inp_lport != uh->uh_dport)
continue;
if (inp->inp_laddr.s_addr != INADDR_ANY) {
if (inp->inp_laddr.s_addr !=
ip->ip_dst.s_addr)
continue;
}
if (inp->inp_faddr.s_addr != INADDR_ANY) {
if (inp->inp_faddr.s_addr !=
ip->ip_src.s_addr ||
inp->inp_fport != uh->uh_sport)
continue;
}
if (last != NULL) {
struct mbuf *n;
if ((n = m_copy(m, 0, M_COPYALL)) != NULL) {
if (last->inp_flags & INP_CONTROLOPTS
|| last->inp_socket->so_options & SO_TIMESTAMP)
ip_savecontrol(last, &opts, ip, n);
if (sbappendaddr(&last->inp_socket->so_rcv,
(struct sockaddr *)&udp_in,
n, opts) == 0) {
m_freem(n);
if (opts)
m_freem(opts);
udpstat.udps_fullsock++;
} else
sorwakeup(last->inp_socket);
opts = 0;
}
}
last = inp;
if ((last->inp_socket->so_options&(SO_REUSEPORT|SO_REUSEADDR)) == 0)
break;
}
if (last == NULL) {
udpstat.udps_noportbcast++;
goto bad;
}
if (last->inp_flags & INP_CONTROLOPTS
|| last->inp_socket->so_options & SO_TIMESTAMP)
ip_savecontrol(last, &opts, ip, m);
if (sbappendaddr(&last->inp_socket->so_rcv,
(struct sockaddr *)&udp_in,
m, opts) == 0) {
udpstat.udps_fullsock++;
goto bad;
}
sorwakeup(last->inp_socket);
return;
}
inp = in_pcblookup_hash(&udbinfo, ip->ip_src, uh->uh_sport,
ip->ip_dst, uh->uh_dport, 1);
if (inp == NULL) {
if (log_in_vain) {
char buf[4*sizeof "123"];
strcpy(buf, inet_ntoa(ip->ip_dst));
log(LOG_INFO,
"Connection attempt to UDP %s:%d from %s:%d\n",
buf, ntohs(uh->uh_dport), inet_ntoa(ip->ip_src),
ntohs(uh->uh_sport));
}
udpstat.udps_noport++;
if (m->m_flags & (M_BCAST | M_MCAST)) {
udpstat.udps_noportbcast++;
goto bad;
}
*ip = save_ip;
if (badport_bandlim(0) < 0)
goto bad;
icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_PORT, 0, 0);
return;
}
udp_in.sin_port = uh->uh_sport;
udp_in.sin_addr = ip->ip_src;
if (inp->inp_flags & INP_CONTROLOPTS
|| inp->inp_socket->so_options & SO_TIMESTAMP)
ip_savecontrol(inp, &opts, ip, m);
iphlen += sizeof(struct udphdr);
m->m_len -= iphlen;
m->m_pkthdr.len -= iphlen;
m->m_data += iphlen;
if (sbappendaddr(&inp->inp_socket->so_rcv, (struct sockaddr *)&udp_in,
m, opts) == 0) {
udpstat.udps_fullsock++;
goto bad;
}
sorwakeup(inp->inp_socket);
return;
bad:
m_freem(m);
if (opts)
m_freem(opts);
}