I am told that, for those of us who have advanced hardware and firmware, the above bridge solution works. But for me, using open source linux firmware ath9k combined with an open source atheros qualcom chip set, the above answer does not work. So, if you try the bridge and you get the 'Operation Not Permitted' error...
WiFi and Ethernet cannot be directly bridged because the package protocols are different. WiFi assumes that every packet comes from another source and includes that information while Ethernet does not. Spoofing/cheating methods exist, but they will be recognized as a threat by the outside world and blocked.
You must create a local network and masquerade packages through it via iptables to properly convert between the two package types and create fully legitimate WiFi packet addressing via Network Address Translation. The above accepted answer saying that the WiFi and Ethernet can be directly bridged is beyond wrong.
This is a bash program with far to many comment lines which creates a WiFi to Ethernet 'bridge':
# Share Wifi with Eth device
# problem: brctl says 'Operation Not Permitted'
# reason: package addressing between Ethernet and Wifi are distinct.
# Ethernet: Sends its IP as the package source.
# Wifi: Sends the previous source IP plus its IP as the sources.
# problem: direct bridges are viewed externally as spoofing and rejected for security reasons.
# Wifi's two source IPs must both be legal and should not be the same or zero.
# need: Network Address Translate packages for proper package modification.
# preliminary: see/create /etc/wpa_supplicant/wpa_supplicant.conf and get your wireless working
# package needed: sudo apt-get -y install dnsmasq # lightweight DNS forwarder and DHCP server
# Step 1: Create the iptables
aEthernet="enp5s0" # the Ethernet output I want for magicJack
aWireless="wlp14s0" # the functioning wireless input
aNext=192.168.2 # the next subnet after 192.168.1
aIp_address="$aNext.1" # use the next subnet to create internal network
aNetmask="255.255.255.0" # the network mask
# aNetwork="$aNext.0" # the external inet representing the internal block
# aBroadcast="$aNext.255" # the broadcast IP
aDhcp_range_start="$aNext.0" # inet addresses to be available in subnet
aDhcp_range_end="$aNext.100" # inet addresses to be available in subnet
aDhcp_time="12h" # address lease duration
sudo iptables --flush
sudo iptables \
--append FORWARD \
--in-interface $aWireless \
--out-interface $aEthernet \
--match state --state RELATED,ESTABLISHED \
sudo iptables \
--append FORWARD \
--in-interface $aEthernet \
--out-interface $aWireless \
sudo iptables --table nat --flush
sudo iptables \
--table nat \
--append POSTROUTING \
--out-interface $aWireless \
# The iptables language:
# iptables understand ipv4 (ip6tables understand ipv6 (not used))
# apt-get install iptables # comes preinstalled with debian
# An iptable stores a chain of rules which redirects packets.
# The first match in a chain determines the packet destination.
# The iptables table name determines when and how the table is used.
# An iptables name is not random or arbitrary.
# --table 'filter' is the default and is for firewall creation
# --table 'mangle' is for chaning packet headers (TTL values...)
# --table 'nat' is for routing packets to different hosts via Network Address Translation
# --table 'raw' is a stateful firewall (knows if packet is part of a new or existing connection...)
# Chains of a table determine the inspection point.
# PREROUTING chain is for arriving packets in tables 'nat', 'mangle' and 'raw'.
# INPUT is chain for packets going to local process in tables 'mangle' and 'filter'.
# OUTPUT is chain for packets from a process in tables 'raw', 'mangle', 'nat', and 'filter'.
# FORWARD is chain for packets routed through localhost in tables 'mangle', 'filter'.
# POSTROUTING is chain for exiting packets in tables 'nat', 'mangle'.
# MASQUERADE chain...
# When it receives a datagram from a computer on the LAN
# it takes note of the type of datagram it is, "TCP," "UDP," "ICMP," etc.
# and modifies the datagram so that it looks like it was generated by the router machine itself
# and remembers that it has done so.
# It then transmits the datagram onto the Internet with its single connected IP address.
# When the destination host receives this datagram,
# it believes the datagram has come from the routing host and sends any reply datagrams back to that address.
# When the Linux masquerade router receives a datagram from its Internet connection,
# it looks in its table of established masqueraded connections
# to see if this datagram actually belongs to a computer on the LAN,
# if it does, it reverses the modification it did on the forward path
# and transmits the datagram to the LAN computer.
# The target of a rule defines what happens to matched packets.
# ACCEPT is the default which forwards or allows the packet.
# DROP acts as if the packet did not exist.
# REJECT responds with an error (then drop).
# LOG creates a kernel log entry (in /var/log/syslog or /var/log/messesages) (then drop).
# The iptables interface:
# sudo iptables
# -L|--list -v|--verbose list current ip table entries (no --table shows filter table)
# -F|--flush empty all ip table entries (no --table flushes filter table)
# -A|--append creates a new rule
# -j|--jump where to send the packet if the packet matches the rule
# -i|--in-interface where packet must come for for a match
# -o|--out-interface where packet must be going to for for a match
# -m|--match state list,of,states allowed or a match
# note: see man iptables for many other types of matching rules
# Packets have a state:
# NEW for the very first packet of a connection
# ESTABLISHED for packets that are part of an existing connection
# RELATED for packets related to another established connection (ftp)
# INVALID for packets whose state is unknown or improper
# UNTRACKED for packets specifically exempted from connection tracking
# DNAT for packets whose destination address was changed by the table
# SNAT for packets whose source address was changed by the table
# Anything you block on the INPUT chain, you can’t access either.
# --state RELATED,ESTABLISHED --jump ACCEPT declares "allow existing connections to continue"
# Save/restore iptables:
# Once declared, iptables may be saved and restored to/from files of your choice.
# iptables-save > iptables.rules # write current rules to configuration file
# iptables-restore < iptables.rules # restore rules from configuration file
# apt-get install iptables-persistent # package for automated iptables-save/restore
# Step 2: Turn on IP forwarding.
sudo sh -c "echo 1 > /proc/sys/net/ipv4/ip_forward"
sudo ifconfig $aEthernet $aIp_address netmask $aNetmask
# May also have to uncomment net.ipv4.ip_forward=1 in /etc/sysctl.conf
# Remove possible default route created by dhcpcd.
# Hide error if route does not exist.
sudo ip route del 0/0 dev $aEthernet &> /dev/null
# Step 3: Reconfigure and restart domain name masquerade.
sudo systemctl stop dnsmasq
cat > /tmp/custom-dnsmasq.conf <<-EOF
sudo rm -r /etc/dnsmasq.d/*
sudo cp /tmp/custom-dnsmasq.conf /etc/dnsmasq.d/custom-dnsmasq.conf
sudo systemctl start dnsmasq
# Show off what we did
echo "$(tput setaf 15)Table filter:$(tput sgr0)"
sudo iptables --list --verbose | sed 's/^/ /'
echo "$(tput setaf 15)Table nat:$(tput sgr0)"
sudo iptables --list --verbose --table nat | sed 's/^/ /'
echo "$(basename "$0"): @see $ sudo iptables -L -n -v # SHOW USAGE METERS ?"
# iptables-save # looking at the output is a good way to verify all the tables
Above is a program needing chmod +x which creates the NAT forwarding 'bridge' when executed. It will not automatically create the 'bridge' when you reboot.
Note: Packages like network-manager, wicd, connman, etc. all do their own thing. Such packages add configuration, GUI, and control layers that convolute everything. Uninstall all of them. This method uses only packages which actually perform a required task: dnsmasq, iptables, and wpasupplicant. The simpler you system, the simpler your solution.
$ ip addr show # list every network interface and its current situation
$ sudo ifup INTERFACE # raise interface declared in /etc/network/interfaces
$ sudo ifdown INTERFACE # lower interface declared in /etc/network/interfaces