Open Bug 871452 Opened 7 years ago Updated 2 years ago

Network Data Policy, which allows a user to control which web app can use 3g (or Wifi)

Categories

(Core :: Networking, defect, P5)

All
Gonk (Firefox OS)
defect

Tracking

()

UNCONFIRMED

People

(Reporter: sa-komorita, Unassigned)

References

Details

(Whiteboard: [necko-would-take])

Attachments

(3 files)

In Firefox OS workweek held in April, Mozilla and KDDI discussed network features and agreed with continuing discussion on bugzilla (or dev-b2g ML). One of their features is “network data policy”, which allows a user to control which web app can use 3g (or wifi) for cost control. 

The attached patches are our prototype for B2G v1.1.0.0-prerelease. You can see “Network Data Policy” in settings app and select a policy. “Never” is default policy, which does not control network interface usage. “Use WiFi in selected apps” is a custom policy in which a user can select apps that use only WiFi even when 3G available. “Use 3G only in Messaging” is a cost saving policy, which make almost apps use Wifi only.

Our patches consist of the following three parts.
- necko part
  We implemented enforcing policy and traffic metering per web-app in necko because we think necko is suitable layer to control sockets. This function is exposed to DOM part as “@mozilla.org/network/connecttable-service;1”.
- DOM part
  The above necko part just includes minimum functions. Other functions (e.g. interfaces to settings apps and initial setting on start-up) are implemented in DOM and running as “ConnectivityService”
- Settings app part
  This is a UI to control network data policy which is exposed to a user. In order to reduce the impact on Web API, we used preferences to exchange the settings between app and dom.

This bus is also related to the following bugs. In our prototype, “metering per web-app” and “enforcing policy” are realized.
- Bug 746065 - (b2g-metering-usage) Tracking: Metering and usage policies for "web apps" 
- Bug 746073 - Meter network usage per "web app"
- Bug 746074 - Allow enforcing network-usage policies on "web apps"
Note: Metering per web-app by each interface (3g or wifi) is not supported yet.
Comment on attachment 748691 [details] [diff] [review]
A patch file for gecko

Change file type to 'patch' to make it easier to read. :)
Attachment #748691 - Attachment is patch: true
Comment on attachment 748692 [details] [diff] [review]
A patch file for gaia

Change file type to 'patch' to make it easier to read. :)
Attachment #748692 - Attachment is patch: true
Hi all,
To make it easy to get a grasp of our patches, I attaches the design architecture with some comments by reference to Network Metering Architecture v2 (https://bug784575.bugzilla.mozilla.org/attachment.cgi?id=753182). The current design is aim at avoiding an large impact on gecko implementation and making independent service(connectTableService). Of cource, I think it's also possible to merge their features into current codes such as NetworkStatsService because their places in gecko are similar.
Depends on: 784575
Hi,
this bug is related to
- Bug 746074 per-app network policies
- Bug 784575 per-app network metering

In addition to just restrict the connection type an app can use, the network usage of an app can also be considered in the rule. Thefore I think this bug can be integrated with Bug 746074.

Currently I'm working at Bug 784575 which supports saving/loading per-app network usage
the next step is looking for the place to catch netowrk data (initially I plan to hook at different protocol (Http/Ftp..,etc.))
after seeing your patch, I wonder if I can simply catch data at SocketTransport
(but seems the appid still shoule be obtained in protocol layer in advance)
do you have any advice? 

We can keeps in contact to see how to integrate/connect these works together : )

Thanks
Yes, you can. In our patch, the metering per app is also implemented (without Saving/loading).

As you say, the SocketTransport needs to obtain appid from protocol layer. But we think that it is a reasonable way in the current situation.


When we designed the proposed architecture, the place of implementation was one of important topics. Protocol layer can know the appid of each traffic (such as nsHttpChannel), however, cannot know and control the network interface of the traffic. On the other hand, socket layer can know the network interface, but cannot know appid...

Thus there were mainly two options for implementation.
1, Socket layer exposes control functions of network interface to protocol layers.
2, Socket layer gets and knows appid (and rules) from network layers.

On basis of the principle of network layering, each layer should be independent and transparent to lower/upper layers. Currently, protocol layer already knows the upper layer information (appid), thus it would be a principled way that socket layer also knows the information as common information among all layers.
In addition, the target of metering and interface control is “packet” per app. Thus socket layer would be suitable layer where the function is implemented and the implementation can be simple as you say.
 
FYI. In android, each process of application sends/receives packets directly. Then socket layer (including other processes) can know app based on the information from Linux kernel, which has a mapping between sockets and userids (unique userid is assigned to each app in android). However, it is OS dependent way.


It's a pleasure to keep in touch and we'll also do our best to see the good way for their works  :) 
Best Regards,
Thanks for your reply!
It's really a great help. 

I think the first step we can do is to separate the part of collecting per app data from your patch. Then we can discuss the interface between NetworkStatsService & ConnectivityService (ex. ConnectivityService can get per app data from NetworkStatsService)

We need a general way to collect per app data from different protocols (that is, collect in socket layer as you explain).
I'll refer your patch to write a patch on Bug 746073 (maybe next week, since I'm not available this week)

please feel free to give any advice !
Thanks,
Hi, just a simple question here.
how do you get the network interface per socket?
Hi, I used SO_BINDTODEVICE option of setsockopt to bind socket to the network interface. By using the option, all packets of the socket are sent from the network interface and we can know which interface is used.

Without the option, it is difficult to know the network interface of socket because the network interface changes depending on routing table (and priority)of the linux kernel even after tcp connection is established. 

Thus, metering per web-app by each interface (3g or wifi) is not supported yet in this implementation.
# now we used SO_BINDTODEVICE for indicated sockets only. 

If in a nonstrict way, we can estimate the network interface per socket based on the routing table (and iptables) of linux kernel.



FYI. In other OSes such as windows, there is strict binding between sockets and network interfaces. Thus if the interface is turned off, tcp connections on the network interface are disconnected. but in linux, the connections are kept and seem to try to use other network interfaces.
After several survey, now I can use NetworkManager [1] to get the active network interface in SocketTransport.

The method of using routing table is not suitable to FFOS due to the design (it may need kernel support and thus not a general solution).

Another question, according to your patch, can I simply expose appid to a SocketTransport when it is initialized? I wonder if a SocketTransport can be reused by more than one apps.. (I remember there is a pool mechanism), do you know about it?

Our goal is to adopt the same architecture both in Network policy and per-app traffic metering and then these two bugs can easily integrated.

Thanks,
Yes. I was also wondering if we can implement the functions in the OS independent ways.

If there is only one active network on FFOS, your idea works well, I also think. However, if there are two or more network interfaces, packets can go through other network interfaces based on the routing table of the OS. In my opinion, I think it would be better to consider the architecture which can support multiple network interfaces because almost OSes such as iOS, android, windows.. can use multiple network interfaces at the same time.
What do you think about it?


Now in my patch, a SocketTransport is reused by only one app. In my opinion, a SocketTransport should be reused by one app because the traffic control should be done for each web app.
For example, other external software such as anti-virus, firewall, and also monitoring tools generally expect that each app has independent tcp connections. Otherwise, they can not identify which web app does suspicious communication, and harmless web apps which use the same tcp connection are involved into regulation.

I totally agree with your policy. This patch is one of prototype and example.  I also continue to survey and try to find out the best way for network ddata policy and per-app traffic metering.

Best Regards
Depends on: 746073
No longer depends on: 784575
Comment on attachment 748691 [details] [diff] [review]
A patch file for gecko

Review of attachment 748691 [details] [diff] [review]:
-----------------------------------------------------------------

Satoshi san - this is pretty neat, but I'm afraid that correlating sockets to applications just isn't going to work. Applications can share sockets and its awesome when they do.

In the case of classic HTTP/1, persistent connections can reuse sockets for different apps. For old style HTTP/1 the problem is actually fairly solvable - you can assign the appropriate app id (connection table?) to the socket at the time a transaction is dispatched onto it instead of just at socket creation time.. but that doesn't work for HTTP pipelines (where the pipeline may consist of multiple applications) or for SPDY (where multiple applications are actively muxed all together) in those cases the socket is actually dealing with multiple applications simultaneously in the same sense that a network interface is dealing with multiple applications simultaneously.

While we could "fix" this by not sharing sockets across apps, we want to promote this mixing. So I don't support ending the sharing. Using an established socket for a new transaction is in every way superior to creating a new one. (save ~3 round trips of latency for SSL, using busier sockets creates bigger congestion windows which more effectively utilize bandwidth and have better loss replacement characteristics, etc etc..).

You can see this on google.com right now.. the same SPDY socket can be used for searching, downloading safe-browsing updates, getting gmail updates, and posting a new g+ update all at the same time.

If we ran a SPDY proxy server for b2g (the way chrome for android does), pretty much all the traffic on your phone would run on 1 or 2 tunnels (sockets) in total.

I can offer you a fairly easy tweak if you can withstand a little sloppiness in the accounting. Specifically in your patch if you were downloading a URI and crossed the quota your download would stop in mid stream.. in my proposal any URI would be completely loaded (if at the time of submission there was quota left over) or would be rejected/queued - they would not be partially downloaded. That means you could slightly run over quota. Whether or not that seems like a good idea probably has to do with how big the quotas realistically are. (a little slop on a 1GB quota is no big deal, a little slop on a 250KB quota is a disaster.) You could do this right in the connection manager or in the http channel.

If that doesn't work for you then you probably need to insturment the reads and writes in nsHttpTransaction instead of in the socket.

It's not clear to me how a transaction that gets stalled with WOULD_BLOCK gets started again.

I'm fine with the notion of a socket binding to a particular interface when being opened. (I would make sure your test coverage includes DNS if that's part of the goal).

Making that part of the http connection info is ok too. You need to add it to the hash key of the connection info to deal with the persistent connection reuse case.

there are a lot of whitespace and smart pointer issues in the patches.. I know its just a WIP but I wanted to make sure to generically note them so they can be cleaned up before a review iteration.

hope that helps.
> I can offer you a fairly easy tweak if you can withstand a little sloppiness 
> in the accounting.... a little slop on a 1GB quota is no big deal, a little 
> slop on a 250KB quota is a disaster.

Apparently some internet plans in some of our markets are quite small (I forget how small--100s of MB/month, possibly less?), but I still suspect that we're fine with Patrick's proposal (i.e. not cutting off loads mid-stream when we hit quota and just failing future loads).  The big counterexamples would be 1) downloading large files (which might include watching videos), and 2) app updates (which IIRC are not done, at least not silently, unless the phone has a wifi connection).  Those could be a problem.

jonas, do you have a sense of how strict the cutoff here needs to be?
Flags: needinfo?(jonas)
for the large file issue, another option is to perform the check in channel::OnDataAvailable().. and asyncopen()..  that will get you pretty close on the byte count modulo a few events backing up in our queues.. (note that the even the socket level implementation won't be perfectly accurate due to TCP rwin, TCP/IP overhead, etc..)..

This frankly is even better imo.. the socket/connection/transaction code should focus on the protocol and routing details (in this case that includes binding the socket to an interface so that part is ok) and the channel code is best suited to cope with mozilla policy stuff.

I think the "only on wifi" style check should definitely be done at the channel level (or higher) as a pre-admission check.If you do it somewhere lower you could end spinning up the 3G radio for DNS or something like that quite accidentally.
hi, Patrick

Thank you for your exact advises. 
My primary concern was whether the socket binding method was acceptable for mozilla implementation policy or not. I’m relieved to hear your comments.

I also have a grasp on importance of SPDY. The reduction of tcp connections in a mobile network is one of important issues. As you mentioned, SPDY and socket sharing across the web apps are not supported in my current patch. But I think they are implementable and I’ll do it. DNS is one of difficult issues because linux kernel doesn't use different DNS server for each network interface. Now we are trying to solve it.

Quota issue is involved with the above socket sharing because lower layer (at the socket) cannot distinguish the web app if several web apps use the same socket. Thus metering and quota need to be done at upper layer (e.g. at the channel level) if we keep socket sharing across web apps. In that case, lower layer (socket/connection) would need to exposes network interface information to upper layer, and the upper layer would use/create different sockets (connection) or stop the request based on the network data policy (e.g. only on wifi)

Basically, lower level (socket) quota control is more accurate than upper layer (channel). But even the lower level control cannot realize perfect control due to socket buffer and re-transmission, and so on, as you mentioned. Thus, I’m fine with your proposal (channel level quota control) to realize more functional quota control. Of course we would need to consider the policy of quota control (how strict, and exception)

Best Regards,
> I think the "only on wifi" style check should definitely be done at the channel 
> level (or higher) as a pre-admission check.

One wrinkle is that I'm not sure whether we want to fail cache hits or not when an app is choked off from using non-wifi networking--that would require us to at least proceed to the point where we know we can use cache or not.  We clearly need to allow appcache hits, but I'm not sure about regular HTTP cache.  It seems weird to allow them, since it's likely to result in broken pages (where only some elements were cached). OTOH maybe it's better than nothing? Perhaps :sicking has an opinion on that too.
(In reply to Patrick McManus [:mcmanus] from comment #13)
> Comment on attachment 748691 [details] [diff] [review]
> A patch file for gecko
> 
> Review of attachment 748691 [details] [diff] [review]:
> -----------------------------------------------------------------
> 
> Satoshi san - this is pretty neat, but I'm afraid that correlating sockets
> to applications just isn't going to work. Applications can share sockets and
> its awesome when they do.
> 
> In the case of classic HTTP/1, persistent connections can reuse sockets for
> different apps. For old style HTTP/1 the problem is actually fairly solvable
> - you can assign the appropriate app id (connection table?) to the socket at
> the time a transaction is dispatched onto it instead of just at socket
> creation time.. but that doesn't work for HTTP pipelines (where the pipeline
> may consist of multiple applications) or for SPDY (where multiple
> applications are actively muxed all together) in those cases the socket is
> actually dealing with multiple applications simultaneously in the same sense
> that a network interface is dealing with multiple applications
> simultaneously.
> 

does sharing socket only happen in other protocol (such as ftp, web socket)? If it only happens in http, is it possible to expose information like appid to socket when each socket I/O session start? (That is, during the socket sharing, it is possible to distinguish the socket I/O session?) I just wonder if there is still a way to do some hack without impact original mechanism and we still can metering on socket layer. Otherwise we'll move the work back to channel/protocol layer

Thanks,

> While we could "fix" this by not sharing sockets across apps, we want to
> promote this mixing. So I don't support ending the sharing. Using an
> established socket for a new transaction is in every way superior to
> creating a new one. (save ~3 round trips of latency for SSL, using busier
> sockets creates bigger congestion windows which more effectively utilize
> bandwidth and have better loss replacement characteristics, etc etc..).
> 
> You can see this on google.com right now.. the same SPDY socket can be used
> for searching, downloading safe-browsing updates, getting gmail updates, and
> posting a new g+ update all at the same time.
> 
> If we ran a SPDY proxy server for b2g (the way chrome for android does),
> pretty much all the traffic on your phone would run on 1 or 2 tunnels
> (sockets) in total.
> 
> I can offer you a fairly easy tweak if you can withstand a little sloppiness
> in the accounting. Specifically in your patch if you were downloading a URI
> and crossed the quota your download would stop in mid stream.. in my
> proposal any URI would be completely loaded (if at the time of submission
> there was quota left over) or would be rejected/queued - they would not be
> partially downloaded. That means you could slightly run over quota. Whether
> or not that seems like a good idea probably has to do with how big the
> quotas realistically are. (a little slop on a 1GB quota is no big deal, a
> little slop on a 250KB quota is a disaster.) You could do this right in the
> connection manager or in the http channel.
> 
> If that doesn't work for you then you probably need to insturment the reads
> and writes in nsHttpTransaction instead of in the socket.
> 
> It's not clear to me how a transaction that gets stalled with WOULD_BLOCK
> gets started again.
> 
> I'm fine with the notion of a socket binding to a particular interface when
> being opened. (I would make sure your test coverage includes DNS if that's
> part of the goal).
> 
> Making that part of the http connection info is ok too. You need to add it
> to the hash key of the connection info to deal with the persistent
> connection reuse case.
> 
> there are a lot of whitespace and smart pointer issues in the patches.. I
> know its just a WIP but I wanted to make sure to generically note them so
> they can be cleaned up before a review iteration.
> 
> hope that helps.
(In reply to John Shih from comment #18)
> (In reply to Patrick McManus [:mcmanus] from comment #13)
> >
> 
> does sharing socket only happen in other protocol (such as ftp, web socket)?

basically its only an HTTP phenomenon.. though you can proxy FTP over HTTP.

> If it only happens in http, is it possible to expose information like appid
> to socket when each socket I/O session start? 

not in the general case. At the time of an I/O event the appid could be known for basic HTTP/1 persistent connections (because they can only have 1 request/response outstanding at a time), but for pipelines or SPDY a single I/O event it could apply to multiple IDs and the only way you know which ones is by pulling them apart in the protocol code.. that's why I suggested the channel as the right place to do this - the protocol code has already done this work for you at this point.

I think exception handling would also be a big problem working at the socket level. Let's say you have a SPDY socket and you did somehow know that the read of 4KB was attributed to an APPID that had just reached its quota. How do you deal with it? You can't return WOULD_BLOCK or some other error code - that will impact all the other applications using the same socket even though they don't have a quota problem.
(In reply to Patrick McManus [:mcmanus] from comment #19)
> (In reply to John Shih from comment #18)
> > (In reply to Patrick McManus [:mcmanus] from comment #13)
> > >
> > 
> > does sharing socket only happen in other protocol (such as ftp, web socket)?
> 
> basically its only an HTTP phenomenon.. though you can proxy FTP over HTTP.
> 
> > If it only happens in http, is it possible to expose information like appid
> > to socket when each socket I/O session start? 
> 
> not in the general case. At the time of an I/O event the appid could be
> known for basic HTTP/1 persistent connections (because they can only have 1
> request/response outstanding at a time), but for pipelines or SPDY a single
> I/O event it could apply to multiple IDs and the only way you know which
> ones is by pulling them apart in the protocol code.. that's why I suggested
> the channel as the right place to do this - the protocol code has already
> done this work for you at this point.
> 
> I think exception handling would also be a big problem working at the socket
> level. Let's say you have a SPDY socket and you did somehow know that the
> read of 4KB was attributed to an APPID that had just reached its quota. How
> do you deal with it? You can't return WOULD_BLOCK or some other error code -
> that will impact all the other applications using the same socket even
> though they don't have a quota problem.

thanks for clarifying, I'll step back to focus on protocol layer.
In some of our target markets, having a 5MB quota per month is the normal data plans. So I'd definitely be worried that always letting any started request to finish can run you over quota too much.

I think a little slop is ok. But more in the order of a few kB here and there. I.e. I would expect that even if we were to cancel a given HTTP connection, that we might receive a few more packets before we've had time to cancel the current connection. This is likely even more the case in SPDY where multiple HTTP connections can be in flight at the same time over a single TCP connection. Such slop seems fine with me.
Flags: needinfo?(jonas)
Whiteboard: [necko-would-take]
Bulk change to priority: https://bugzilla.mozilla.org/show_bug.cgi?id=1399258
Priority: -- → P5
You need to log in before you can comment on or make changes to this bug.