For a number of reasons:
- It used to be impossible to implement securely. Until very recently, there was no hardware support for virtualizing the network buffers, which would mean emulating the network hardware in userspace. This would be very slow.
- Because even today, many devices don't have the required hardware virtualization support. For example, many (most?) ARM devices. If you give direct DMA access, you might be allowing anyone to splatter whatever code they want across whatever memory they want.
- Without kernel arbitration of some sort, there's no way to do load balancing across services, throttle, or firewall effectively.
- The kernel is designed to provide a uniform interface for all programs to the hardware. Putting networking in userspace gets rid of this abstraction, and means that every program has to be aware of the network hardware; software that works with file descriptors directly can't use the same abstraction for files and network.
- Because there are no hardware limitations on the amount of multiplexing. I don't remember how much muxing various hardware supports, but hardware has limits for this sort of thing. If you want more than N processes using the virtual network, you might be SOL with a virtualized userspace network stack.