Commit 8836286d authored by David S. Miller's avatar David S. Miller

Merge nuts.ninka.net:/home/davem/src/BK/network-2.5

into nuts.ninka.net:/home/davem/src/BK/net-2.5
parents 6b29059a 4e75852f
......@@ -531,6 +531,14 @@ M: mike@i-Connect.Net
L: linux-eata@i-connect.net, linux-scsi@vger.kernel.org
S: Maintained
EBTABLES
P: Bart De Schuymer
M: bart.de.schuymer@pandora.be
L: ebtables-user@lists.sourceforge.net
L: ebtables-devel@lists.sourceforge.net
W: http://ebtables.sourceforge.net/
S: Maintained
EEPRO100 NETWORK DRIVER
P: Andrey V. Savochkin
M: saw@saw.sw.com.sg
......
......@@ -55,8 +55,21 @@ csum_partial:
movl 20(%esp),%eax # Function arg: unsigned int sum
movl 16(%esp),%ecx # Function arg: int len
movl 12(%esp),%esi # Function arg: unsigned char *buff
testl $2, %esi # Check alignment.
testl $3, %esi # Check alignment.
jz 2f # Jump if alignment is ok.
testl $1, %esi # Check alignment.
jz 10f # Jump if alignment is boundary of 2bytes.
# buf is odd
dec %ecx
jl 8f
movzbl (%esi), %ebx
adcl %ebx, %eax
roll $8, %eax
inc %esi
testl $2, %esi
jz 2f
10:
subl $2, %ecx # Alignment uses up two bytes.
jae 1f # Jump if we had at least two bytes.
addl $2, %ecx # ecx was < 2. Deal with it.
......@@ -111,6 +124,10 @@ csum_partial:
6: addl %ecx,%eax
adcl $0, %eax
7:
testl $1, 12(%esp)
jz 8f
roll $8, %eax
8:
popl %ebx
popl %esi
ret
......@@ -126,8 +143,8 @@ csum_partial:
movl 16(%esp),%ecx # Function arg: int len
movl 12(%esp),%esi # Function arg: const unsigned char *buf
testl $2, %esi
jnz 30f
testl $3, %esi
jnz 25f
10:
movl %ecx, %edx
movl %ecx, %ebx
......@@ -145,6 +162,19 @@ csum_partial:
lea 2(%esi), %esi
adcl $0, %eax
jmp 10b
25:
testl $1, %esi
jz 30f
# buf is odd
dec %ecx
jl 90f
movzbl (%esi), %ebx
addl %ebx, %eax
adcl $0, %eax
roll $8, %eax
inc %esi
testl $2, %esi
jz 10b
30: subl $2, %ecx
ja 20b
......@@ -211,6 +241,10 @@ csum_partial:
addl %ebx,%eax
adcl $0,%eax
80:
testl $1, 12(%esp)
jz 90f
roll $8, %eax
90:
popl %ebx
popl %esi
ret
......
......@@ -234,7 +234,26 @@ static void tg3_enable_ints(struct tg3 *tp)
tw32(GRC_LOCAL_CTRL,
tp->grc_local_ctrl | GRC_LCLCTRL_SETINT);
}
#if 0
tr32(MAILBOX_INTERRUPT_0 + TG3_64BIT_REG_LOW);
#endif
}
static void tg3_switch_clocks(struct tg3 *tp)
{
if (tr32(TG3PCI_CLOCK_CTRL) & CLOCK_CTRL_44MHZ_CORE) {
tw32(TG3PCI_CLOCK_CTRL,
(CLOCK_CTRL_44MHZ_CORE | CLOCK_CTRL_ALTCLK));
tr32(TG3PCI_CLOCK_CTRL);
udelay(40);
tw32(TG3PCI_CLOCK_CTRL,
(CLOCK_CTRL_ALTCLK));
tr32(TG3PCI_CLOCK_CTRL);
udelay(40);
}
tw32(TG3PCI_CLOCK_CTRL, 0);
tr32(TG3PCI_CLOCK_CTRL);
udelay(40);
}
#define PHY_BUSY_LOOPS 5000
......@@ -443,10 +462,12 @@ static int tg3_set_power_state(struct tg3 *tp, int state)
tp->link_config.orig_autoneg = tp->link_config.autoneg;
}
if (tp->phy_id != PHY_ID_SERDES) {
tp->link_config.speed = SPEED_10;
tp->link_config.duplex = DUPLEX_HALF;
tp->link_config.autoneg = AUTONEG_ENABLE;
tg3_setup_phy(tp);
}
tg3_halt(tp);
......@@ -455,6 +476,7 @@ static int tg3_set_power_state(struct tg3 *tp, int state)
if (tp->tg3_flags & TG3_FLAG_WOL_ENABLE) {
u32 mac_mode;
if (tp->phy_id != PHY_ID_SERDES) {
tg3_writephy(tp, MII_TG3_AUX_CTRL, 0x5a);
udelay(40);
......@@ -463,6 +485,10 @@ static int tg3_set_power_state(struct tg3 *tp, int state)
if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5700 ||
!(tp->tg3_flags & TG3_FLAG_WOL_SPEED_100MB))
mac_mode |= MAC_MODE_LINK_POLARITY;
} else {
mac_mode = MAC_MODE_PORT_MODE_TBI;
}
if (((power_caps & PCI_PM_CAP_PME_D3cold) &&
(tp->tg3_flags & TG3_FLAG_WOL_ENABLE)))
......@@ -470,7 +496,7 @@ static int tg3_set_power_state(struct tg3 *tp, int state)
tw32(MAC_MODE, mac_mode);
tr32(MAC_MODE);
udelay(40);
udelay(100);
tw32(MAC_RX_MODE, RX_MODE_ENABLE);
tr32(MAC_RX_MODE);
......@@ -4385,6 +4411,8 @@ static int tg3_init_hw(struct tg3 *tp)
if (err)
goto out;
tg3_switch_clocks(tp);
tw32(TG3PCI_MEM_WIN_BASE_ADDR, 0);
err = tg3_reset_hw(tp);
......@@ -5259,6 +5287,11 @@ static int tg3_ethtool_ioctl (struct net_device *dev, void *useraddr)
return -EFAULT;
if (wol.wolopts & ~WAKE_MAGIC)
return -EINVAL;
if ((wol.wolopts & WAKE_MAGIC) &&
tp->phy_id == PHY_ID_SERDES &&
!(tp->tg3_flags & TG3_FLAG_SERDES_WOL_CAP))
return -EINVAL;
spin_lock_irq(&tp->lock);
if (wol.wolopts & WAKE_MAGIC)
tp->tg3_flags |= TG3_FLAG_WOL_ENABLE;
......@@ -5793,6 +5826,8 @@ static int __devinit tg3_phy_probe(struct tg3 *tp)
if (nic_cfg & NIC_SRAM_DATA_CFG_ASF_ENABLE)
tp->tg3_flags |= TG3_FLAG_ENABLE_ASF;
if (nic_cfg & NIC_SRAM_DATA_CFG_FIBER_WOL)
tp->tg3_flags |= TG3_FLAG_SERDES_WOL_CAP;
}
/* Now read the physical PHY_ID from the chip and verify
......@@ -6131,8 +6166,9 @@ static int __devinit tg3_get_invariants(struct tg3 *tp)
/* Initialize data/descriptor byte/word swapping. */
tw32(GRC_MODE, tp->grc_mode);
/* Clear these out for sanity. */
tw32(TG3PCI_CLOCK_CTRL, 0);
tg3_switch_clocks(tp);
/* Clear this out for sanity. */
tw32(TG3PCI_MEM_WIN_BASE_ADDR, 0);
pci_read_config_dword(tp->pdev, TG3PCI_PCISTATE,
......
......@@ -1274,6 +1274,7 @@
#define NIC_SRAM_DATA_CFG_WOL_ENABLE 0x00000040
#define NIC_SRAM_DATA_CFG_ASF_ENABLE 0x00000080
#define NIC_SRAM_DATA_CFG_EEPROM_WP 0x00000100
#define NIC_SRAM_DATA_CFG_FIBER_WOL 0x00004000
#define NIC_SRAM_DATA_PHY_ID 0x00000b74
#define NIC_SRAM_DATA_PHY_ID1_MASK 0xffff0000
......@@ -1779,7 +1780,7 @@ struct tg3 {
#define TG3_FLAG_PCI_32BIT 0x00080000
#define TG3_FLAG_NO_TX_PSEUDO_CSUM 0x00100000
#define TG3_FLAG_NO_RX_PSEUDO_CSUM 0x00200000
#define TG3_FLAG_AUTONEG_DISABLE 0x00400000
#define TG3_FLAG_SERDES_WOL_CAP 0x00400000
#define TG3_FLAG_JUMBO_ENABLE 0x00800000
#define TG3_FLAG_10_100_ONLY 0x01000000
#define TG3_FLAG_PAUSE_AUTONEG 0x02000000
......
......@@ -102,7 +102,8 @@ struct net_bridge;
struct net_bridge_port;
extern int (*br_ioctl_hook)(unsigned long arg);
extern void (*br_handle_frame_hook)(struct sk_buff *skb);
extern int (*br_handle_frame_hook)(struct sk_buff *skb);
extern int (*br_should_route_hook)(struct sk_buff **pskb);
#endif
......
......@@ -18,7 +18,18 @@
#define NF_BR_LOCAL_OUT 3
/* Packets about to hit the wire. */
#define NF_BR_POST_ROUTING 4
#define NF_BR_NUMHOOKS 5
/* Not really a hook, but used for the ebtables broute table */
#define NF_BR_BROUTING 5
#define NF_BR_NUMHOOKS 6
enum nf_br_hook_priorities {
NF_BR_PRI_FIRST = INT_MIN,
NF_BR_PRI_FILTER_BRIDGED = -200,
NF_BR_PRI_FILTER_OTHER = 200,
NF_BR_PRI_NAT_DST_BRIDGED = -300,
NF_BR_PRI_NAT_DST_OTHER = 100,
NF_BR_PRI_NAT_SRC = 300,
NF_BR_PRI_LAST = INT_MAX,
};
#endif
......@@ -65,6 +65,9 @@ if [ "$CONFIG_DECNET" != "n" ]; then
source net/decnet/Config.in
fi
dep_tristate '802.1d Ethernet Bridging' CONFIG_BRIDGE $CONFIG_INET
if [ "$CONFIG_BRIDGE" != "n" -a "$CONFIG_NETFILTER" != "n" ]; then
source net/bridge/netfilter/Config.in
fi
if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
tristate 'CCITT X.25 Packet Layer (EXPERIMENTAL)' CONFIG_X25
tristate 'LAPB Data Link Driver (EXPERIMENTAL)' CONFIG_LAPB
......
......@@ -2,10 +2,13 @@
# Makefile for the IEEE 802.1d ethernet bridging layer.
#
export-objs := br.o
obj-$(CONFIG_BRIDGE) += bridge.o
bridge-objs := br.o br_device.o br_fdb.o br_forward.o br_if.o br_input.o \
br_ioctl.o br_notify.o br_stp.o br_stp_bpdu.o \
br_stp_if.o br_stp_timer.o
obj-$(CONFIG_BRIDGE_NF_EBTABLES) += netfilter/
include $(TOPDIR)/Rules.make
......@@ -28,6 +28,8 @@
#include "../atm/lec.h"
#endif
int (*br_should_route_hook) (struct sk_buff **pskb) = NULL;
void br_dec_use_count()
{
MOD_DEC_USE_COUNT;
......@@ -74,6 +76,8 @@ static void __exit br_deinit(void)
#endif
}
EXPORT_SYMBOL(br_should_route_hook);
module_init(br_init)
module_exit(br_deinit)
MODULE_LICENSE("GPL");
......@@ -49,6 +49,9 @@ static int __br_forward_finish(struct sk_buff *skb)
static void __br_deliver(struct net_bridge_port *to, struct sk_buff *skb)
{
skb->dev = to->dev;
#ifdef CONFIG_NETFILTER_DEBUG
skb->nf_debug = 0;
#endif
NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_OUT, skb, NULL, skb->dev,
__br_forward_finish);
}
......
......@@ -24,6 +24,9 @@ unsigned char bridge_ula[6] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 };
static int br_pass_frame_up_finish(struct sk_buff *skb)
{
#ifdef CONFIG_NETFILTER_DEBUG
skb->nf_debug = 0;
#endif
netif_rx(skb);
return 0;
......@@ -112,7 +115,7 @@ static int br_handle_frame_finish(struct sk_buff *skb)
return 0;
}
void br_handle_frame(struct sk_buff *skb)
int br_handle_frame(struct sk_buff *skb)
{
struct net_bridge *br;
unsigned char *dest;
......@@ -146,25 +149,29 @@ void br_handle_frame(struct sk_buff *skb)
goto handle_special_frame;
if (p->state == BR_STATE_FORWARDING) {
if (br_should_route_hook && br_should_route_hook(&skb))
return -1;
NF_HOOK(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
br_handle_frame_finish);
read_unlock(&br->lock);
return;
return 0;
}
err:
read_unlock(&br->lock);
err_nolock:
kfree_skb(skb);
return;
return 0;
handle_special_frame:
if (!dest[5]) {
br_stp_handle_bpdu(skb);
read_unlock(&br->lock);
return;
return 0;
}
kfree_skb(skb);
read_unlock(&br->lock);
return 0;
}
......@@ -166,7 +166,7 @@ extern void br_get_port_ifindices(struct net_bridge *br,
int *ifindices);
/* br_input.c */
extern void br_handle_frame(struct sk_buff *skb);
extern int br_handle_frame(struct sk_buff *skb);
/* br_ioctl.c */
extern void br_call_ioctl_atomic(void (*fn)(void));
......
CONFIG_BRIDGE_EBT
ebtables is an extendable frame filtering system for the Linux
Ethernet bridge. Its usage and implementation is very similar to that
of iptables.
The difference is that ebtables works on the Link Layer, while iptables
works on the Network Layer. ebtables can filter all frames that come
into contact with a logical bridge device.
Apart from filtering, ebtables also allows MAC source and destination
alterations (we call it MAC SNAT and MAC DNAT) and also provides
functionality for making Linux a brouter.
If you want to compile it as a module, say M here and read
<file:Documentation/modules.txt>. If unsure, say `N'.
CONFIG_BRIDGE_EBT_T_FILTER
The ebtables filter table is used to define frame filtering rules at
local input, forwarding and local output. See the man page for
ebtables(8).
If you want to compile it as a module, say M here and read
<file:Documentation/modules.txt>. If unsure, say `N'.
CONFIG_BRIDGE_EBT_T_NAT
The ebtables nat table is used to define rules that alter the MAC
source address (MAC SNAT) or the MAC destination address (MAC DNAT).
See the man page for ebtables(8).
If you want to compile it as a module, say M here and read
<file:Documentation/modules.txt>. If unsure, say `N'.
CONFIG_BRIDGE_EBT_BROUTE
The ebtables broute table is used to define rules that decide between
bridging and routing frames, giving Linux the functionality of a
brouter. See the man page for ebtables(8) and examples on the ebtables
website.
If you want to compile it as a module, say M here and read
<file:Documentation/modules.txt>. If unsure, say `N'.
CONFIG_BRIDGE_EBT_LOG
This option adds the log target, that you can use in any rule in
any ebtables table. It records the frame header to the syslog.
If you want to compile it as a module, say M here and read
<file:Documentation/modules.txt>. If unsure, say `N'.
CONFIG_BRIDGE_EBT_IPF
This option adds the IP match, which allows basic IP header field
filtering.
If you want to compile it as a module, say M here and read
<file:Documentation/modules.txt>. If unsure, say `N'.
CONFIG_BRIDGE_EBT_ARPF
This option adds the ARP match, which allows ARP and RARP header field
filtering.
If you want to compile it as a module, say M here and read
<file:Documentation/modules.txt>. If unsure, say `N'.
CONFIG_BRIDGE_EBT_VLANF
This option adds the 802.1Q vlan match, which allows the filtering of
802.1Q vlan fields.
If you want to compile it as a module, say M here and read
<file:Documentation/modules.txt>. If unsure, say `N'.
CONFIG_BRIDGE_EBT_MARKF
This option adds the mark match, which allows matching frames based on
the 'nfmark' value in the frame. This can be set by the mark target.
This value is the same as the one used in the iptables mark match and
target.
If you want to compile it as a module, say M here and read
<file:Documentation/modules.txt>. If unsure, say `N'.
CONFIG_BRIDGE_EBT_SNAT
This option adds the MAC SNAT target, which allows altering the MAC
source address of frames.
If you want to compile it as a module, say M here and read
<file:Documentation/modules.txt>. If unsure, say `N'.
CONFIG_BRIDGE_EBT_DNAT
This option adds the MAC DNAT target, which allows altering the MAC
destination address of frames.
If you want to compile it as a module, say M here and read
<file:Documentation/modules.txt>. If unsure, say `N'.
CONFIG_BRIDGE_EBT_REDIRECT
This option adds the MAC redirect target, which allows altering the MAC
destination address of a frame to that of the device it arrived on.
If you want to compile it as a module, say M here and read
<file:Documentation/modules.txt>. If unsure, say `N'.
CONFIG_BRIDGE_EBT_MARK_T
This option adds the mark target, which allows marking frames by
setting the 'nfmark' value in the frame.
This value is the same as the one used in the iptables mark match and
target.
If you want to compile it as a module, say M here and read
<file:Documentation/modules.txt>. If unsure, say `N'.
#
# Bridge netfilter configuration
#
dep_tristate ' Bridge: ebtables' CONFIG_BRIDGE_NF_EBTABLES $CONFIG_BRIDGE
dep_tristate ' ebt: filter table support' CONFIG_BRIDGE_EBT_T_FILTER $CONFIG_BRIDGE_NF_EBTABLES
dep_tristate ' ebt: nat table support' CONFIG_BRIDGE_EBT_T_NAT $CONFIG_BRIDGE_NF_EBTABLES
dep_tristate ' ebt: broute table support' CONFIG_BRIDGE_EBT_BROUTE $CONFIG_BRIDGE_NF_EBTABLES
dep_tristate ' ebt: log support' CONFIG_BRIDGE_EBT_LOG $CONFIG_BRIDGE_NF_EBTABLES
dep_tristate ' ebt: IP filter support' CONFIG_BRIDGE_EBT_IPF $CONFIG_BRIDGE_NF_EBTABLES
dep_tristate ' ebt: ARP filter support' CONFIG_BRIDGE_EBT_ARPF $CONFIG_BRIDGE_NF_EBTABLES
dep_tristate ' ebt: 802.1Q VLAN filter support (EXPERIMENTAL)' CONFIG_BRIDGE_EBT_VLANF $CONFIG_BRIDGE_NF_EBTABLES
dep_tristate ' ebt: mark filter support' CONFIG_BRIDGE_EBT_MARKF $CONFIG_BRIDGE_NF_EBTABLES
dep_tristate ' ebt: snat target support' CONFIG_BRIDGE_EBT_SNAT $CONFIG_BRIDGE_NF_EBTABLES
dep_tristate ' ebt: dnat target support' CONFIG_BRIDGE_EBT_DNAT $CONFIG_BRIDGE_NF_EBTABLES
dep_tristate ' ebt: redirect target support' CONFIG_BRIDGE_EBT_REDIRECT $CONFIG_BRIDGE_NF_EBTABLES
dep_tristate ' ebt: mark target support' CONFIG_BRIDGE_EBT_MARK_T $CONFIG_BRIDGE_NF_EBTABLES
#
# Makefile for the netfilter modules for Link Layer filtering on a bridge.
#
export-objs := ebtables.o
obj-$(CONFIG_BRIDGE_NF_EBTABLES) += ebtables.o
obj-$(CONFIG_BRIDGE_EBT_T_FILTER) += ebtable_filter.o
obj-$(CONFIG_BRIDGE_EBT_T_NAT) += ebtable_nat.o
obj-$(CONFIG_BRIDGE_EBT_BROUTE) += ebtable_broute.o
obj-$(CONFIG_BRIDGE_EBT_IPF) += ebt_ip.o
obj-$(CONFIG_BRIDGE_EBT_ARPF) += ebt_arp.o
obj-$(CONFIG_BRIDGE_EBT_VLANF) += ebt_vlan.o
obj-$(CONFIG_BRIDGE_EBT_MARKF) += ebt_mark_m.o
obj-$(CONFIG_BRIDGE_EBT_LOG) += ebt_log.o
obj-$(CONFIG_BRIDGE_EBT_SNAT) += ebt_snat.o
obj-$(CONFIG_BRIDGE_EBT_DNAT) += ebt_dnat.o
obj-$(CONFIG_BRIDGE_EBT_REDIRECT) += ebt_redirect.o
obj-$(CONFIG_BRIDGE_EBT_MARK_T) += ebt_mark.o
include $(TOPDIR)/Rules.make
/*
* ebt_arp
*
* Authors:
* Bart De Schuymer <bart.de.schuymer@pandora.be>
* Tim Gardner <timg@tpi.com>
*
* April, 2002
*
*/
#include <linux/netfilter_bridge/ebtables.h>
#include <linux/netfilter_bridge/ebt_arp.h>
#include <linux/if_arp.h>
#include <linux/module.h>
static int ebt_filter_arp(const struct sk_buff *skb, const struct net_device *in,
const struct net_device *out, const void *data, unsigned int datalen)
{
struct ebt_arp_info *info = (struct ebt_arp_info *)data;
if (info->bitmask & EBT_ARP_OPCODE && FWINV(info->opcode !=
((*skb).nh.arph)->ar_op, EBT_ARP_OPCODE))
return EBT_NOMATCH;
if (info->bitmask & EBT_ARP_HTYPE && FWINV(info->htype !=
((*skb).nh.arph)->ar_hrd, EBT_ARP_HTYPE))
return EBT_NOMATCH;
if (info->bitmask & EBT_ARP_PTYPE && FWINV(info->ptype !=
((*skb).nh.arph)->ar_pro, EBT_ARP_PTYPE))
return EBT_NOMATCH;
if (info->bitmask & (EBT_ARP_SRC_IP | EBT_ARP_DST_IP))
{
uint32_t arp_len = sizeof(struct arphdr) +
(2 * (((*skb).nh.arph)->ar_hln)) +
(2 * (((*skb).nh.arph)->ar_pln));
uint32_t dst;
uint32_t src;
// Make sure the packet is long enough.
if ((((*skb).nh.raw) + arp_len) > (*skb).tail)
return EBT_NOMATCH;
// IPv4 addresses are always 4 bytes.
if (((*skb).nh.arph)->ar_pln != sizeof(uint32_t))
return EBT_NOMATCH;
if (info->bitmask & EBT_ARP_SRC_IP) {
memcpy(&src, ((*skb).nh.raw) + sizeof(struct arphdr) +
((*skb).nh.arph)->ar_hln, sizeof(uint32_t));
if (FWINV(info->saddr != (src & info->smsk),
EBT_ARP_SRC_IP))
return EBT_NOMATCH;
}
if (info->bitmask & EBT_ARP_DST_IP) {
memcpy(&dst, ((*skb).nh.raw)+sizeof(struct arphdr) +
(2*(((*skb).nh.arph)->ar_hln)) +
(((*skb).nh.arph)->ar_pln), sizeof(uint32_t));
if (FWINV(info->daddr != (dst & info->dmsk),
EBT_ARP_DST_IP))
return EBT_NOMATCH;
}
}
return EBT_MATCH;
}
static int ebt_arp_check(const char *tablename, unsigned int hookmask,
const struct ebt_entry *e, void *data, unsigned int datalen)
{
struct ebt_arp_info *info = (struct ebt_arp_info *)data;
if (datalen != sizeof(struct ebt_arp_info))
return -EINVAL;
if ((e->ethproto != __constant_htons(ETH_P_ARP) &&
e->ethproto != __constant_htons(ETH_P_RARP)) ||
e->invflags & EBT_IPROTO)
return -EINVAL;
if (info->bitmask & ~EBT_ARP_MASK || info->invflags & ~EBT_ARP_MASK)
return -EINVAL;
return 0;
}
static struct ebt_match filter_arp =
{
{NULL, NULL}, EBT_ARP_MATCH, ebt_filter_arp, ebt_arp_check, NULL,
THIS_MODULE
};
static int __init init(void)
{
return ebt_register_match(&filter_arp);
}
static void __exit fini(void)
{
ebt_unregister_match(&filter_arp);
}
module_init(init);
module_exit(fini);
MODULE_LICENSE("GPL");
/*
* ebt_dnat
*
* Authors:
* Bart De Schuymer <bart.de.schuymer@pandora.be>
*
* June, 2002
*
*/
#include <linux/netfilter_bridge/ebtables.h>
#include <linux/netfilter_bridge/ebt_nat.h>
#include <linux/module.h>
#include <net/sock.h>
static int ebt_target_dnat(struct sk_buff **pskb, unsigned int hooknr,
const struct net_device *in, const struct net_device *out,
const void *data, unsigned int datalen)
{
struct ebt_nat_info *info = (struct ebt_nat_info *)data;
memcpy(((**pskb).mac.ethernet)->h_dest, info->mac,
ETH_ALEN * sizeof(unsigned char));
return info->target;
}
static int ebt_target_dnat_check(const char *tablename, unsigned int hookmask,
const struct ebt_entry *e, void *data, unsigned int datalen)
{
struct ebt_nat_info *info = (struct ebt_nat_info *)data;
if (BASE_CHAIN && info->target == EBT_RETURN)
return -EINVAL;
CLEAR_BASE_CHAIN_BIT;
if ( (strcmp(tablename, "nat") ||
(hookmask & ~((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_LOCAL_OUT)))) &&
(strcmp(tablename, "broute") || hookmask & ~(1 << NF_BR_BROUTING)) )
return -EINVAL;
if (datalen != sizeof(struct ebt_nat_info))
return -EINVAL;
if (INVALID_TARGET)
return -EINVAL;
return 0;
}
static struct ebt_target dnat =
{
{NULL, NULL}, EBT_DNAT_TARGET, ebt_target_dnat, ebt_target_dnat_check,
NULL, THIS_MODULE
};
static int __init init(void)
{
return ebt_register_target(&dnat);
}
static void __exit fini(void)
{
ebt_unregister_target(&dnat);
}
module_init(init);
module_exit(fini);
MODULE_LICENSE("GPL");
/*
* ebt_ip
*
* Authors:
* Bart De Schuymer <bart.de.schuymer@pandora.be>
*
* April, 2002
*
*/
#include <linux/netfilter_bridge/ebtables.h>
#include <linux/netfilter_bridge/ebt_ip.h>
#include <linux/ip.h>
#include <linux/module.h>
static int ebt_filter_ip(const struct sk_buff *skb, const struct net_device *in,
const struct net_device *out, const void *data,
unsigned int datalen)
{
struct ebt_ip_info *info = (struct ebt_ip_info *)data;
if (info->bitmask & EBT_IP_TOS &&
FWINV(info->tos != ((*skb).nh.iph)->tos, EBT_IP_TOS))
return EBT_NOMATCH;
if (info->bitmask & EBT_IP_PROTO && FWINV(info->protocol !=
((*skb).nh.iph)->protocol, EBT_IP_PROTO))
return EBT_NOMATCH;
if (info->bitmask & EBT_IP_SOURCE &&
FWINV((((*skb).nh.iph)->saddr & info->smsk) !=
info->saddr, EBT_IP_SOURCE))
return EBT_NOMATCH;
if ((info->bitmask & EBT_IP_DEST) &&
FWINV((((*skb).nh.iph)->daddr & info->dmsk) !=
info->daddr, EBT_IP_DEST))
return EBT_NOMATCH;
return EBT_MATCH;
}
static int ebt_ip_check(const char *tablename, unsigned int hookmask,
const struct ebt_entry *e, void *data, unsigned int datalen)
{
struct ebt_ip_info *info = (struct ebt_ip_info *)data;
if (datalen != sizeof(struct ebt_ip_info))
return -EINVAL;
if (e->ethproto != __constant_htons(ETH_P_IP) ||
e->invflags & EBT_IPROTO)
return -EINVAL;
if (info->bitmask & ~EBT_IP_MASK || info->invflags & ~EBT_IP_MASK)
return -EINVAL;
return 0;
}
static struct ebt_match filter_ip =
{
{NULL, NULL}, EBT_IP_MATCH, ebt_filter_ip, ebt_ip_check, NULL,
THIS_MODULE
};
static int __init init(void)
{
return ebt_register_match(&filter_ip);
}
static void __exit fini(void)
{
ebt_unregister_match(&filter_ip);
}
module_init(init);
module_exit(fini);
MODULE_LICENSE("GPL");
/*
* ebt_log
*
* Authors:
* Bart De Schuymer <bart.de.schuymer@pandora.be>
*
* April, 2002
*
*/
#include <linux/netfilter_bridge/ebtables.h>
#include <linux/netfilter_bridge/ebt_log.h>
#include <linux/module.h>
#include <linux/ip.h>
#include <linux/if_arp.h>
#include <linux/spinlock.h>
static spinlock_t ebt_log_lock = SPIN_LOCK_UNLOCKED;
static int ebt_log_check(const char *tablename, unsigned int hookmask,
const struct ebt_entry *e, void *data, unsigned int datalen)
{
struct ebt_log_info *info = (struct ebt_log_info *)data;
if (datalen != sizeof(struct ebt_log_info))
return -EINVAL;
if (info->bitmask & ~EBT_LOG_MASK)
return -EINVAL;
if (info->loglevel >= 8)
return -EINVAL;
info->prefix[EBT_LOG_PREFIX_SIZE - 1] = '\0';
return 0;
}
static void ebt_log(const struct sk_buff *skb, const struct net_device *in,
const struct net_device *out, const void *data, unsigned int datalen)
{
struct ebt_log_info *info = (struct ebt_log_info *)data;
char level_string[4] = "< >";
level_string[1] = '0' + info->loglevel;
spin_lock_bh(&ebt_log_lock);
printk(level_string);
printk("%s IN=%s OUT=%s ", info->prefix, in ? in->name : "",
out ? out->name : "");
if (skb->dev->hard_header_len) {
int i;
unsigned char *p = (skb->mac.ethernet)->h_source;
printk("MAC source = ");
for (i = 0; i < ETH_ALEN; i++,p++)
printk("%02x%c", *p, i == ETH_ALEN - 1 ? ' ':':');
printk("MAC dest = ");
p = (skb->mac.ethernet)->h_dest;
for (i = 0; i < ETH_ALEN; i++,p++)
printk("%02x%c", *p, i == ETH_ALEN - 1 ? ' ':':');
}
printk("proto = 0x%04x", ntohs(((*skb).mac.ethernet)->h_proto));
if ((info->bitmask & EBT_LOG_IP) && skb->mac.ethernet->h_proto ==
htons(ETH_P_IP)){
struct iphdr *iph = skb->nh.iph;
printk(" IP SRC=%u.%u.%u.%u IP DST=%u.%u.%u.%u,",
NIPQUAD(iph->saddr), NIPQUAD(iph->daddr));
printk(" IP tos=0x%02X, IP proto=%d", iph->tos, iph->protocol);
}
if ((info->bitmask & EBT_LOG_ARP) &&
((skb->mac.ethernet->h_proto == __constant_htons(ETH_P_ARP)) ||
(skb->mac.ethernet->h_proto == __constant_htons(ETH_P_RARP)))) {
struct arphdr * arph = skb->nh.arph;
printk(" ARP HTYPE=%d, PTYPE=0x%04x, OPCODE=%d",
ntohs(arph->ar_hrd), ntohs(arph->ar_pro),
ntohs(arph->ar_op));
}
printk("\n");
spin_unlock_bh(&ebt_log_lock);
}
struct ebt_watcher log =
{
{NULL, NULL}, EBT_LOG_WATCHER, ebt_log, ebt_log_check, NULL,
THIS_MODULE
};
static int __init init(void)
{
return ebt_register_watcher(&log);
}
static void __exit fini(void)
{
ebt_unregister_watcher(&log);
}
module_init(init);
module_exit(fini);
MODULE_LICENSE("GPL");
/*
* ebt_mark
*
* Authors:
* Bart De Schuymer <bart.de.schuymer@pandora.be>
*
* July, 2002
*
*/
// The mark target can be used in any chain
// I believe adding a mangle table just for marking is total overkill
// Marking a frame doesn't really change anything in the frame anyway
#include <linux/netfilter_bridge/ebtables.h>
#include <linux/netfilter_bridge/ebt_mark_t.h>
#include <linux/module.h>
static int ebt_target_mark(struct sk_buff **pskb, unsigned int hooknr,
const struct net_device *in, const struct net_device *out,
const void *data, unsigned int datalen)
{
struct ebt_mark_t_info *info = (struct ebt_mark_t_info *)data;
if ((*pskb)->nfmark != info->mark) {
(*pskb)->nfmark = info->mark;
(*pskb)->nfcache |= NFC_ALTERED;
}
return info->target;
}
static int ebt_target_mark_check(const char *tablename, unsigned int hookmask,
const struct ebt_entry *e, void *data, unsigned int datalen)
{
struct ebt_mark_t_info *info = (struct ebt_mark_t_info *)data;
if (datalen != sizeof(struct ebt_mark_t_info))
return -EINVAL;
if (BASE_CHAIN && info->target == EBT_RETURN)
return -EINVAL;
CLEAR_BASE_CHAIN_BIT;
if (INVALID_TARGET)
return -EINVAL;
return 0;
}
static struct ebt_target mark_target =
{
{NULL, NULL}, EBT_MARK_TARGET, ebt_target_mark,
ebt_target_mark_check, NULL, THIS_MODULE
};
static int __init init(void)
{
return ebt_register_target(&mark_target);
}
static void __exit fini(void)
{
ebt_unregister_target(&mark_target);
}
module_init(init);
module_exit(fini);
MODULE_LICENSE("GPL");
/*
* ebt_mark_m
*
* Authors:
* Bart De Schuymer <bart.de.schuymer@pandora.be>
*
* July, 2002
*
*/
#include <linux/netfilter_bridge/ebtables.h>
#include <linux/netfilter_bridge/ebt_mark_m.h>
#include <linux/module.h>
static int ebt_filter_mark(const struct sk_buff *skb,
const struct net_device *in, const struct net_device *out, const void *data,
unsigned int datalen)
{
struct ebt_mark_m_info *info = (struct ebt_mark_m_info *) data;
if (info->bitmask & EBT_MARK_OR)
return !(!!(skb->nfmark & info->mask) ^ info->invert);
return !(((skb->nfmark & info->mask) == info->mark) ^ info->invert);
}
static int ebt_mark_check(const char *tablename, unsigned int hookmask,
const struct ebt_entry *e, void *data, unsigned int datalen)
{
struct ebt_mark_m_info *info = (struct ebt_mark_m_info *) data;
if (datalen != sizeof(struct ebt_mark_m_info))
return -EINVAL;
if (info->bitmask & ~EBT_MARK_MASK)
return -EINVAL;
if ((info->bitmask & EBT_MARK_OR) && (info->bitmask & EBT_MARK_AND))
return -EINVAL;
if (!info->bitmask)
return -EINVAL;
return 0;
}
static struct ebt_match filter_mark =
{
{NULL, NULL}, EBT_MARK_MATCH, ebt_filter_mark, ebt_mark_check, NULL,
THIS_MODULE
};
static int __init init(void)
{
return ebt_register_match(&filter_mark);
}
static void __exit fini(void)
{
ebt_unregister_match(&filter_mark);
}
module_init(init);
module_exit(fini);
MODULE_LICENSE("GPL");
/*
* ebt_redirect
*
* Authors:
* Bart De Schuymer <bart.de.schuymer@pandora.be>
*
* April, 2002
*
*/
#include <linux/netfilter_bridge/ebtables.h>
#include <linux/netfilter_bridge/ebt_redirect.h>
#include <linux/module.h>
#include <net/sock.h>
#include "../br_private.h"
static int ebt_target_redirect(struct sk_buff **pskb, unsigned int hooknr,
const struct net_device *in, const struct net_device *out,
const void *data, unsigned int datalen)
{
struct ebt_redirect_info *info = (struct ebt_redirect_info *)data;
if (hooknr != NF_BR_BROUTING)
memcpy((**pskb).mac.ethernet->h_dest,
in->br_port->br->dev.dev_addr, ETH_ALEN);
else {
memcpy((**pskb).mac.ethernet->h_dest,
in->dev_addr, ETH_ALEN);
(*pskb)->pkt_type = PACKET_HOST;
}
return info->target;
}
static int ebt_target_redirect_check(const char *tablename, unsigned int hookmask,
const struct ebt_entry *e, void *data, unsigned int datalen)
{
struct ebt_redirect_info *info = (struct ebt_redirect_info *)data;
if (datalen != sizeof(struct ebt_redirect_info))
return -EINVAL;
if (BASE_CHAIN && info->target == EBT_RETURN)
return -EINVAL;
CLEAR_BASE_CHAIN_BIT;
if ( (strcmp(tablename, "nat") || hookmask & ~(1 << NF_BR_PRE_ROUTING)) &&
(strcmp(tablename, "broute") || hookmask & ~(1 << NF_BR_BROUTING)) )
return -EINVAL;
if (INVALID_TARGET)
return -EINVAL;
return 0;
}
static struct ebt_target redirect_target =
{
{NULL, NULL}, EBT_REDIRECT_TARGET, ebt_target_redirect,
ebt_target_redirect_check, NULL, THIS_MODULE
};
static int __init init(void)
{
return ebt_register_target(&redirect_target);
}
static void __exit fini(void)
{
ebt_unregister_target(&redirect_target);
}
module_init(init);
module_exit(fini);
MODULE_LICENSE("GPL");
/*
* ebt_snat
*
* Authors:
* Bart De Schuymer <bart.de.schuymer@pandora.be>
*
* June, 2002
*
*/
#include <linux/netfilter_bridge/ebtables.h>
#include <linux/netfilter_bridge/ebt_nat.h>
#include <linux/module.h>
static int ebt_target_snat(struct sk_buff **pskb, unsigned int hooknr,
const struct net_device *in, const struct net_device *out,
const void *data, unsigned int datalen)
{
struct ebt_nat_info *info = (struct ebt_nat_info *) data;
memcpy(((**pskb).mac.ethernet)->h_source, info->mac,
ETH_ALEN * sizeof(unsigned char));
return info->target;
}
static int ebt_target_snat_check(const char *tablename, unsigned int hookmask,
const struct ebt_entry *e, void *data, unsigned int datalen)
{
struct ebt_nat_info *info = (struct ebt_nat_info *) data;
if (datalen != sizeof(struct ebt_nat_info))
return -EINVAL;
if (BASE_CHAIN && info->target == EBT_RETURN)
return -EINVAL;
CLEAR_BASE_CHAIN_BIT;
if (strcmp(tablename, "nat"))
return -EINVAL;
if (hookmask & ~(1 << NF_BR_POST_ROUTING))
return -EINVAL;
if (INVALID_TARGET)
return -EINVAL;
return 0;
}
static struct ebt_target snat =
{
{NULL, NULL}, EBT_SNAT_TARGET, ebt_target_snat, ebt_target_snat_check,
NULL, THIS_MODULE
};
static int __init init(void)
{
return ebt_register_target(&snat);
}
static void __exit fini(void)
{
ebt_unregister_target(&snat);
}
module_init(init);
module_exit(fini);
MODULE_LICENSE("GPL");
This diff is collapsed.
/*
* ebtable_broute
*
* Authors:
* Bart De Schuymer <bart.de.schuymer@pandora.be>
*
* April, 2002
*
* This table lets you choose between routing and bridging for frames
* entering on a bridge enslaved nic. This table is traversed before any
* other ebtables table. See net/bridge/br_input.c.
*/
#include <linux/netfilter_bridge/ebtables.h>
#include <linux/module.h>
#include <linux/if_bridge.h>
#include <linux/brlock.h>
// EBT_ACCEPT means the frame will be bridged
// EBT_DROP means the frame will be routed
static struct ebt_entries initial_chain =
{0, "BROUTING", 0, EBT_ACCEPT, 0};
static struct ebt_replace initial_table =
{
"broute", 1 << NF_BR_BROUTING, 0, sizeof(struct ebt_entries),
{ [NF_BR_BROUTING]&initial_chain}, 0, NULL, (char *)&initial_chain
};
static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
{
if (valid_hooks & ~(1 << NF_BR_BROUTING))
return -EINVAL;
return 0;
}
static struct ebt_table broute_table =
{
{NULL, NULL}, "broute", &initial_table, 1 << NF_BR_BROUTING,
RW_LOCK_UNLOCKED, check, NULL
};
static int ebt_broute(struct sk_buff **pskb)
{
int ret;
ret = ebt_do_table(NF_BR_BROUTING, pskb, (*pskb)->dev, NULL,
&broute_table);
if (ret == NF_DROP)
return 1; // route it
return 0; // bridge it
}
static int __init init(void)
{
int ret;
ret = ebt_register_table(&broute_table);
if (ret < 0)
return ret;
br_write_lock_bh(BR_NETPROTO_LOCK);
// see br_input.c
br_should_route_hook = ebt_broute;
br_write_unlock_bh(BR_NETPROTO_LOCK);
return ret;
}
static void __exit fini(void)
{
br_write_lock_bh(BR_NETPROTO_LOCK);
br_should_route_hook = NULL;
br_write_unlock_bh(BR_NETPROTO_LOCK);
ebt_unregister_table(&broute_table);
}
module_init(init);
module_exit(fini);
MODULE_LICENSE("GPL");
/*
* ebtable_filter
*
* Authors:
* Bart De Schuymer <bart.de.schuymer@pandora.be>
*
* April, 2002
*
*/
#include <linux/netfilter_bridge/ebtables.h>
#include <linux/module.h>
#define FILTER_VALID_HOOKS ((1 << NF_BR_LOCAL_IN) | (1 << NF_BR_FORWARD) | \
(1 << NF_BR_LOCAL_OUT))
static struct ebt_entries initial_chains[] =
{
{0, "INPUT", 0, EBT_ACCEPT, 0},
{0, "FORWARD", 0, EBT_ACCEPT, 0},
{0, "OUTPUT", 0, EBT_ACCEPT, 0}
};
static struct ebt_replace initial_table =
{
"filter", FILTER_VALID_HOOKS, 0, 3 * sizeof(struct ebt_entries),
{ [NF_BR_LOCAL_IN]&initial_chains[0], [NF_BR_FORWARD]&initial_chains[1],
[NF_BR_LOCAL_OUT]&initial_chains[2] }, 0, NULL, (char *)initial_chains
};
static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
{
if (valid_hooks & ~FILTER_VALID_HOOKS)
return -EINVAL;
return 0;
}
static struct ebt_table frame_filter =
{
{NULL, NULL}, "filter", &initial_table, FILTER_VALID_HOOKS,
RW_LOCK_UNLOCKED, check, NULL
};
static unsigned int
ebt_hook (unsigned int hook, struct sk_buff **pskb, const struct net_device *in,
const struct net_device *out, int (*okfn)(struct sk_buff *))
{
return ebt_do_table(hook, pskb, in, out, &frame_filter);
}
static struct nf_hook_ops ebt_ops_filter[] = {
{ { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_LOCAL_IN,
NF_BR_PRI_FILTER_BRIDGED},
{ { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_FORWARD,
NF_BR_PRI_FILTER_BRIDGED},
{ { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_LOCAL_OUT,
NF_BR_PRI_FILTER_OTHER}
};
static int __init init(void)
{
int i, j, ret;
ret = ebt_register_table(&frame_filter);
if (ret < 0)
return ret;
for (i = 0; i < sizeof(ebt_ops_filter) / sizeof(ebt_ops_filter[0]); i++)
if ((ret = nf_register_hook(&ebt_ops_filter[i])) < 0)
goto cleanup;
return ret;
cleanup:
for (j = 0; j < i; j++)
nf_unregister_hook(&ebt_ops_filter[j]);
ebt_unregister_table(&frame_filter);
return ret;
}
static void __exit fini(void)
{
int i;
for (i = 0; i < sizeof(ebt_ops_filter) / sizeof(ebt_ops_filter[0]); i++)
nf_unregister_hook(&ebt_ops_filter[i]);
ebt_unregister_table(&frame_filter);
}
module_init(init);
module_exit(fini);
MODULE_LICENSE("GPL");
/*
* ebtable_nat
*
* Authors:
* Bart De Schuymer <bart.de.schuymer@pandora.be>
*
* April, 2002
*
*/
#include <linux/netfilter_bridge/ebtables.h>
#include <linux/module.h>
#define NAT_VALID_HOOKS ((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_LOCAL_OUT) | \
(1 << NF_BR_POST_ROUTING))
static struct ebt_entries initial_chains[] =
{
{0, "PREROUTING", 0, EBT_ACCEPT, 0},
{0, "OUTPUT", 0, EBT_ACCEPT, 0},
{0, "POSTROUTING", 0, EBT_ACCEPT, 0}
};
static struct ebt_replace initial_table =
{
"nat", NAT_VALID_HOOKS, 0, 3 * sizeof(struct ebt_entries),
{ [NF_BR_PRE_ROUTING]&initial_chains[0], [NF_BR_LOCAL_OUT]&initial_chains[1],
[NF_BR_POST_ROUTING]&initial_chains[2] }, 0, NULL, (char *)initial_chains
};
static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
{
if (valid_hooks & ~NAT_VALID_HOOKS)
return -EINVAL;
return 0;
}
static struct ebt_table frame_nat =
{
{NULL, NULL}, "nat", &initial_table, NAT_VALID_HOOKS,
RW_LOCK_UNLOCKED, check, NULL
};
static unsigned int
ebt_nat_dst(unsigned int hook, struct sk_buff **pskb, const struct net_device *in
, const struct net_device *out, int (*okfn)(struct sk_buff *))
{
return ebt_do_table(hook, pskb, in, out, &frame_nat);
}
static unsigned int
ebt_nat_src(unsigned int hook, struct sk_buff **pskb, const struct net_device *in
, const struct net_device *out, int (*okfn)(struct sk_buff *))
{
return ebt_do_table(hook, pskb, in, out, &frame_nat);
}
static struct nf_hook_ops ebt_ops_nat[] = {
{ { NULL, NULL }, ebt_nat_dst, PF_BRIDGE, NF_BR_LOCAL_OUT,
NF_BR_PRI_NAT_DST_OTHER},
{ { NULL, NULL }, ebt_nat_src, PF_BRIDGE, NF_BR_POST_ROUTING,
NF_BR_PRI_NAT_SRC},
{ { NULL, NULL }, ebt_nat_dst, PF_BRIDGE, NF_BR_PRE_ROUTING,
NF_BR_PRI_NAT_DST_BRIDGED},
};
static int __init init(void)
{
int i, ret, j;
ret = ebt_register_table(&frame_nat);
if (ret < 0)
return ret;
for (i = 0; i < sizeof(ebt_ops_nat) / sizeof(ebt_ops_nat[0]); i++)
if ((ret = nf_register_hook(&ebt_ops_nat[i])) < 0)
goto cleanup;
return ret;
cleanup:
for (j = 0; j < i; j++)
nf_unregister_hook(&ebt_ops_nat[j]);
ebt_unregister_table(&frame_nat);
return ret;
}
static void __exit fini(void)
{
int i;
for (i = 0; i < sizeof(ebt_ops_nat) / sizeof(ebt_ops_nat[0]); i++)
nf_unregister_hook(&ebt_ops_nat[i]);
ebt_unregister_table(&frame_nat);
}
module_init(init);
module_exit(fini);
MODULE_LICENSE("GPL");
This diff is collapsed.
......@@ -1397,7 +1397,7 @@ void net_call_rx_atomic(void (*fn)(void))
}
#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
void (*br_handle_frame_hook)(struct sk_buff *skb) = NULL;
int (*br_handle_frame_hook)(struct sk_buff *skb) = NULL;
#endif
static __inline__ int handle_bridge(struct sk_buff *skb,
......@@ -1414,7 +1414,6 @@ static __inline__ int handle_bridge(struct sk_buff *skb,
}
}
br_handle_frame_hook(skb);
return ret;
}
......@@ -1475,7 +1474,12 @@ int netif_receive_skb(struct sk_buff *skb)
#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
if (skb->dev->br_port && br_handle_frame_hook) {
return handle_bridge(skb, pt_prev);
int ret;
ret = handle_bridge(skb, pt_prev);
if (br_handle_frame_hook(skb) == 0)
return ret;
pt_prev = NULL;
}
#endif
......
......@@ -878,18 +878,22 @@ static int llc_ui_recvmsg(struct socket *sock, struct msghdr *msg, int size,
if (!skb) /* shutdown */
goto out;
copied = skb->len;
if (copied > size) {
if (copied > size)
copied = size;
msg->msg_flags |= MSG_TRUNC;
}
rc = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
if (rc)
goto dgram_free;
if (skb->len > copied) {
skb_pull(skb, copied);
skb_queue_head(&sk->receive_queue, skb);
}
if (uaddr)
memcpy(uaddr, llc_ui_skb_cb(skb), sizeof(*uaddr));
msg->msg_namelen = sizeof(*uaddr);
if (!skb->list) {
dgram_free:
kfree_skb(skb);
}
out:
release_sock(sk);
return rc ? : copied;
......@@ -915,7 +919,7 @@ static int llc_ui_sendmsg(struct socket *sock, struct msghdr *msg, int len,
int noblock = flags & MSG_DONTWAIT;
struct net_device *dev;
struct sk_buff *skb;
int rc = -EINVAL, size = 0;
int rc = -EINVAL, size = 0, copied = 0, hdrlen;
dprintk("%s: sending from %02X to %02X\n", __FUNCTION__, llc->laddr.lsap, llc->daddr.lsap);
lock_sock(sk);
......@@ -943,20 +947,26 @@ static int llc_ui_sendmsg(struct socket *sock, struct msghdr *msg, int len,
goto release;
} else
dev = llc->dev;
size = dev->hard_header_len + len + llc_ui_header_len(sk, addr);
rc = -EMSGSIZE;
hdrlen = dev->hard_header_len + llc_ui_header_len(sk, addr);
size = hdrlen + len;
if (size > dev->mtu)
goto release;
size = dev->mtu;
copied = size - hdrlen;
skb = sock_alloc_send_skb(sk, size, noblock, &rc);
if (!skb)
goto release;
skb->sk = sk;
skb->dev = dev;
skb->protocol = llc_proto_type(addr->sllc_arphrd);
skb_reserve(skb, dev->hard_header_len + llc_ui_header_len(sk, addr));
rc = memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len);
skb_reserve(skb, hdrlen);
rc = memcpy_fromiovec(skb_put(skb, copied), msg->msg_iov, copied);
if (rc)
goto out;
if (sk->type == SOCK_DGRAM || addr->sllc_ua) {
llc_build_and_send_ui_pkt(llc->sap, skb, addr->sllc_dmac,
addr->sllc_dsap);
goto out;
}
if (addr->sllc_test) {
llc_build_and_send_test_pkt(llc->sap, skb, addr->sllc_dmac,
addr->sllc_dsap);
......@@ -967,11 +977,6 @@ static int llc_ui_sendmsg(struct socket *sock, struct msghdr *msg, int len,
addr->sllc_dsap);
goto out;
}
if (sk->type == SOCK_DGRAM || addr->sllc_ua) {
llc_build_and_send_ui_pkt(llc->sap, skb, addr->sllc_dmac,
addr->sllc_dsap);
goto out;
}
rc = -ENOPROTOOPT;
if (!(sk->type == SOCK_STREAM && !addr->sllc_ua))
goto out;
......@@ -986,7 +991,7 @@ static int llc_ui_sendmsg(struct socket *sock, struct msghdr *msg, int len,
dprintk("%s: failed sending from %02X to %02X: %d\n",
__FUNCTION__, llc->laddr.lsap, llc->daddr.lsap, rc);
release_sock(sk);
return rc ? : len;
return rc ? : copied;
}
/**
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment