132026Salex/*
244558Sbrian * natd - Network Address Translation Daemon for FreeBSD.
344558Sbrian *
432107Salex * This software is provided free of charge, with no
532107Salex * warranty of any kind, either expressed or implied.
632107Salex * Use at your own risk.
732107Salex *
844558Sbrian * You may copy, modify and distribute this software (icmp.c) freely.
932107Salex *
1032107Salex * Ari Suutari <suutari@iki.fi>
1132107Salex *
1250476Speter * $FreeBSD$
1332026Salex */
1432026Salex
1526781Sbrian#include <stdlib.h>
1626781Sbrian#include <stdio.h>
1726781Sbrian#include <unistd.h>
1826781Sbrian#include <string.h>
1926781Sbrian#include <ctype.h>
2026781Sbrian
2126781Sbrian#include <sys/types.h>
2226781Sbrian#include <sys/socket.h>
2326781Sbrian#include <sys/time.h>
2426781Sbrian#include <errno.h>
2526781Sbrian#include <signal.h>
2626781Sbrian
2726781Sbrian#include <netdb.h>
2826781Sbrian
2926781Sbrian#include <netinet/in.h>
3026781Sbrian#include <netinet/in_systm.h>
3126781Sbrian#include <netinet/ip.h>
3226781Sbrian#include <netinet/ip_icmp.h>
3326781Sbrian#include <machine/in_cksum.h>
3426781Sbrian
3526781Sbrian#include <alias.h>
3626781Sbrian
3726781Sbrian#include "natd.h"
3826781Sbrian
3926781Sbrianint SendNeedFragIcmp (int sock, struct ip* failedDgram, int mtu)
4026781Sbrian{
4126781Sbrian	char			icmpBuf[IP_MAXPACKET];
4226781Sbrian	struct ip*		ip;
4326781Sbrian	struct icmp*		icmp;
4426781Sbrian	int			icmpLen;
4526781Sbrian	int			failBytes;
4626781Sbrian	int			failHdrLen;
4726781Sbrian	struct sockaddr_in	addr;
4826781Sbrian	int			wrote;
4926781Sbrian	struct in_addr		swap;
5026781Sbrian/*
5126781Sbrian * Don't send error if packet is
5226781Sbrian * not the first fragment.
5326781Sbrian */
5426781Sbrian	if (ntohs (failedDgram->ip_off) & ~(IP_MF | IP_DF))
5526781Sbrian		return 0;
5626781Sbrian/*
5726781Sbrian * Dont respond if failed datagram is ICMP.
5826781Sbrian */
5926781Sbrian	if (failedDgram->ip_p == IPPROTO_ICMP)
6026781Sbrian		return 0;
6126781Sbrian/*
6226781Sbrian * Start building the message.
6326781Sbrian */
6426781Sbrian	ip   = (struct ip*) icmpBuf;
6526781Sbrian	icmp = (struct icmp*) (icmpBuf + sizeof (struct ip));
6626781Sbrian/*
6726781Sbrian * Complete ICMP part.
6826781Sbrian */
6926781Sbrian	icmp->icmp_type  	= ICMP_UNREACH;
7026781Sbrian	icmp->icmp_code		= ICMP_UNREACH_NEEDFRAG;
7126781Sbrian	icmp->icmp_cksum	= 0;
7226781Sbrian	icmp->icmp_void		= 0;
7326781Sbrian	icmp->icmp_nextmtu	= htons (mtu);
7426781Sbrian/*
7526781Sbrian * Copy header + 64 bits of original datagram.
7626781Sbrian */
7726781Sbrian	failHdrLen = (failedDgram->ip_hl << 2);
7826781Sbrian	failBytes  = failedDgram->ip_len - failHdrLen;
7926781Sbrian	if (failBytes > 8)
8026781Sbrian		failBytes = 8;
8126781Sbrian
8226781Sbrian	failBytes += failHdrLen;
8326781Sbrian	icmpLen    = ICMP_MINLEN + failBytes;
8426781Sbrian
8526781Sbrian	memcpy (&icmp->icmp_ip, failedDgram, failBytes);
8626781Sbrian/*
8726781Sbrian * Calculate checksum.
8826781Sbrian */
89131567Sphk	icmp->icmp_cksum = LibAliasInternetChecksum (mla, (u_short*) icmp,
9028045Sbrian							icmpLen);
9126781Sbrian/*
9226781Sbrian * Add IP header using old IP header as template.
9326781Sbrian */
9426781Sbrian	memcpy (ip, failedDgram, sizeof (struct ip));
9526781Sbrian
9626781Sbrian	ip->ip_v	= 4;
9726781Sbrian	ip->ip_hl	= 5;
9826781Sbrian	ip->ip_len	= htons (sizeof (struct ip) + icmpLen);
9926781Sbrian	ip->ip_p	= IPPROTO_ICMP;
10026781Sbrian	ip->ip_tos	= 0;
10126781Sbrian
10226781Sbrian	swap = ip->ip_dst;
10326781Sbrian	ip->ip_dst = ip->ip_src;
10426781Sbrian	ip->ip_src = swap;
10526781Sbrian
106131567Sphk	LibAliasIn (mla, (char*) ip, IP_MAXPACKET);
10726781Sbrian
10826781Sbrian	addr.sin_family		= AF_INET;
10926781Sbrian	addr.sin_addr		= ip->ip_dst;
11026781Sbrian	addr.sin_port		= 0;
11126781Sbrian/*
11226781Sbrian * Put packet into processing queue.
11326781Sbrian */
11426781Sbrian	wrote = sendto (sock,
11526781Sbrian		        icmp,
11626781Sbrian	    		icmpLen,
11726781Sbrian	    		0,
11826781Sbrian	    		(struct sockaddr*) &addr,
11926781Sbrian	    		sizeof addr);
12026781Sbrian
12126781Sbrian	if (wrote != icmpLen)
12226781Sbrian		Warn ("Cannot send ICMP message.");
12326781Sbrian
12426781Sbrian	return 1;
12526781Sbrian}
12626781Sbrian
12726781Sbrian
128